2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % http://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
89 /* Suppress libpng pedantic warnings that were added in
90 * libpng-1.2.41 and libpng-1.4.0. If you are working on
91 * migration to libpng-1.5, remove these defines and then
92 * fix any code that generates warnings.
94 /* #define PNG_DEPRECATED Use of this function is deprecated */
95 /* #define PNG_USE_RESULT The result of this function must be checked */
96 /* #define PNG_NORETURN This function does not return */
97 /* #define PNG_ALLOCATED The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
106 /* ImageMagick differences */
107 #define first_scene scene
109 #if PNG_LIBPNG_VER > 10011
111 Optional declarations. Define or undefine them as you like.
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
116 Features under construction. Define these to work on them.
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127 (((color).red == (target).red) && \
128 ((color).green == (target).green) && \
129 ((color).blue == (target).blue))
132 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
140 const struct sRGB_info_struct sRGB_info[] =
142 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143 { 3048, 0x3b8772b9UL, 0},
145 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146 { 3052, 0x427ebb21UL, 1},
148 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149 {60988, 0x306fd8aeUL, 0},
151 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152 {60960, 0xbbef7812UL, 0},
154 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155 { 3024, 0x5d5129ceUL, 1},
157 /* HP-Microsoft sRGB v2 perceptual */
158 { 3144, 0x182ea552UL, 0},
160 /* HP-Microsoft sRGB v2 media-relative */
161 { 3144, 0xf29e526dUL, 1},
163 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164 { 524, 0xd4938c39UL, 0},
166 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167 { 3212, 0x034af5a1UL, 0},
170 { 0, 0x00000000UL, 0},
173 /* Macros for left-bit-replication to ensure that pixels
174 * and PixelInfos all have the same image->depth, and for use
175 * in PNG8 quantization.
178 /* LBR01: Replicate top bit */
180 #define LBR01PacketRed(pixelpacket) \
181 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
184 #define LBR01PacketGreen(pixelpacket) \
185 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
188 #define LBR01PacketBlue(pixelpacket) \
189 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
192 #define LBR01PacketAlpha(pixelpacket) \
193 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
196 #define LBR01PacketRGB(pixelpacket) \
198 LBR01PacketRed((pixelpacket)); \
199 LBR01PacketGreen((pixelpacket)); \
200 LBR01PacketBlue((pixelpacket)); \
203 #define LBR01PacketRGBO(pixelpacket) \
205 LBR01PacketRGB((pixelpacket)); \
206 LBR01PacketAlpha((pixelpacket)); \
209 #define LBR01PixelRed(pixel) \
210 (SetPixelRed(image, \
211 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212 0 : QuantumRange,(pixel)));
214 #define LBR01PixelGreen(pixel) \
215 (SetPixelGreen(image, \
216 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217 0 : QuantumRange,(pixel)));
219 #define LBR01PixelBlue(pixel) \
220 (SetPixelBlue(image, \
221 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222 0 : QuantumRange,(pixel)));
224 #define LBR01PixelAlpha(pixel) \
225 (SetPixelAlpha(image, \
226 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227 0 : QuantumRange,(pixel)));
229 #define LBR01PixelRGB(pixel) \
231 LBR01PixelRed((pixel)); \
232 LBR01PixelGreen((pixel)); \
233 LBR01PixelBlue((pixel)); \
236 #define LBR01PixelRGBA(pixel) \
238 LBR01PixelRGB((pixel)); \
239 LBR01PixelAlpha((pixel)); \
242 /* LBR02: Replicate top 2 bits */
244 #define LBR02PacketRed(pixelpacket) \
246 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247 (pixelpacket).red=ScaleCharToQuantum( \
248 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
250 #define LBR02PacketGreen(pixelpacket) \
252 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253 (pixelpacket).green=ScaleCharToQuantum( \
254 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
256 #define LBR02PacketBlue(pixelpacket) \
258 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259 (pixelpacket).blue=ScaleCharToQuantum( \
260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
262 #define LBR02PacketAlpha(pixelpacket) \
264 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265 (pixelpacket).alpha=ScaleCharToQuantum( \
266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
269 #define LBR02PacketRGB(pixelpacket) \
271 LBR02PacketRed((pixelpacket)); \
272 LBR02PacketGreen((pixelpacket)); \
273 LBR02PacketBlue((pixelpacket)); \
276 #define LBR02PacketRGBO(pixelpacket) \
278 LBR02PacketRGB((pixelpacket)); \
279 LBR02PacketAlpha((pixelpacket)); \
282 #define LBR02PixelRed(pixel) \
284 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
286 SetPixelRed(image, ScaleCharToQuantum( \
287 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
290 #define LBR02PixelGreen(pixel) \
292 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
294 SetPixelGreen(image, ScaleCharToQuantum( \
295 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
298 #define LBR02PixelBlue(pixel) \
300 unsigned char lbr_bits= \
301 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302 SetPixelBlue(image, ScaleCharToQuantum( \
303 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
306 #define LBR02PixelAlpha(pixel) \
308 unsigned char lbr_bits= \
309 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310 SetPixelAlpha(image, ScaleCharToQuantum( \
311 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
315 #define LBR02PixelRGB(pixel) \
317 LBR02PixelRed((pixel)); \
318 LBR02PixelGreen((pixel)); \
319 LBR02PixelBlue((pixel)); \
322 #define LBR02PixelRGBA(pixel) \
324 LBR02PixelRGB((pixel)); \
325 LBR02PixelAlpha((pixel)); \
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329 PNG8 quantization) */
331 #define LBR03PacketRed(pixelpacket) \
333 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334 (pixelpacket).red=ScaleCharToQuantum( \
335 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
337 #define LBR03PacketGreen(pixelpacket) \
339 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340 (pixelpacket).green=ScaleCharToQuantum( \
341 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
343 #define LBR03PacketBlue(pixelpacket) \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346 (pixelpacket).blue=ScaleCharToQuantum( \
347 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
350 #define LBR03PacketRGB(pixelpacket) \
352 LBR03PacketRed((pixelpacket)); \
353 LBR03PacketGreen((pixelpacket)); \
354 LBR03PacketBlue((pixelpacket)); \
357 #define LBR03PixelRed(pixel) \
359 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
361 SetPixelRed(image, ScaleCharToQuantum( \
362 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
364 #define LBR03Green(pixel) \
366 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
368 SetPixelGreen(image, ScaleCharToQuantum( \
369 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
371 #define LBR03Blue(pixel) \
373 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
375 SetPixelBlue(image, ScaleCharToQuantum( \
376 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
379 #define LBR03RGB(pixel) \
381 LBR03PixelRed((pixel)); \
382 LBR03Green((pixel)); \
383 LBR03Blue((pixel)); \
386 /* LBR04: Replicate top 4 bits */
388 #define LBR04PacketRed(pixelpacket) \
390 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
393 #define LBR04PacketGreen(pixelpacket) \
395 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
398 #define LBR04PacketBlue(pixelpacket) \
400 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
403 #define LBR04PacketAlpha(pixelpacket) \
405 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
409 #define LBR04PacketRGB(pixelpacket) \
411 LBR04PacketRed((pixelpacket)); \
412 LBR04PacketGreen((pixelpacket)); \
413 LBR04PacketBlue((pixelpacket)); \
416 #define LBR04PacketRGBO(pixelpacket) \
418 LBR04PacketRGB((pixelpacket)); \
419 LBR04PacketAlpha((pixelpacket)); \
422 #define LBR04PixelRed(pixel) \
424 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
427 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
429 #define LBR04PixelGreen(pixel) \
431 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
433 SetPixelGreen(image,\
434 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
436 #define LBR04PixelBlue(pixel) \
438 unsigned char lbr_bits= \
439 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
441 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
443 #define LBR04PixelAlpha(pixel) \
445 unsigned char lbr_bits= \
446 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447 SetPixelAlpha(image,\
448 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
451 #define LBR04PixelRGB(pixel) \
453 LBR04PixelRed((pixel)); \
454 LBR04PixelGreen((pixel)); \
455 LBR04PixelBlue((pixel)); \
458 #define LBR04PixelRGBA(pixel) \
460 LBR04PixelRGB((pixel)); \
461 LBR04PixelAlpha((pixel)); \
465 Establish thread safety.
466 setjmp/longjmp is claimed to be safe on these platforms:
467 setjmp/longjmp is alleged to be unsafe on these platforms:
469 #ifdef PNG_SETJMP_SUPPORTED
470 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
471 # define IMPNG_SETJMP_NOT_THREAD_SAFE
474 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
476 *ping_semaphore = (SemaphoreInfo *) NULL;
481 This temporary until I set up malloc'ed object attributes array.
482 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
485 #define MNG_MAX_OBJECTS 256
488 If this not defined, spec is interpreted strictly. If it is
489 defined, an attempt will be made to recover from some errors,
491 o global PLTE too short
496 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
497 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
498 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
499 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
500 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
501 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
502 will be enabled by default in libpng-1.2.0.
504 #ifdef PNG_MNG_FEATURES_SUPPORTED
505 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
506 # define PNG_READ_EMPTY_PLTE_SUPPORTED
508 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
509 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
514 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
515 This macro is only defined in libpng-1.0.3 and later.
516 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
518 #ifndef PNG_UINT_31_MAX
519 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
523 Constant strings for known chunk types. If you need to add a chunk,
524 add a string holding the name here. To make the code more
525 portable, we use ASCII numbers like this, not characters.
528 static png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
529 static png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
530 static png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
531 static png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
532 static png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
533 static png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
534 static png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
535 static png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
536 static png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
537 static png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
538 static png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
539 static png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
540 static png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
541 static png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
542 static png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
543 static png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
544 static png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
545 static png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
546 static png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
547 static png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
548 static png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
549 static png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
550 static png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
551 static png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
552 static png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
553 static png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
554 static png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
555 static png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
556 static png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
557 static png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
558 static png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
559 static png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
560 static png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
561 static png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
563 #if defined(JNG_SUPPORTED)
564 static png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
565 static png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
566 static png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
567 static png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
568 static png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
569 static png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
573 /* Other known chunks that are not yet supported by ImageMagick: */
574 static png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
575 static png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
576 static png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
577 static png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
578 static png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
579 static png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
580 static png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
583 typedef struct _MngBox
592 typedef struct _MngPair
599 #ifdef MNG_OBJECT_BUFFERS
600 typedef struct _MngBuffer
632 typedef struct _MngInfo
635 #ifdef MNG_OBJECT_BUFFERS
637 *ob[MNG_MAX_OBJECTS];
648 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
649 bytes_in_read_buffer,
655 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
656 defined(PNG_MNG_FEATURES_SUPPORTED)
668 have_saved_bkgd_index,
669 have_write_global_chrm,
670 have_write_global_gama,
671 have_write_global_plte,
672 have_write_global_srgb,
686 x_off[MNG_MAX_OBJECTS],
687 y_off[MNG_MAX_OBJECTS];
693 object_clip[MNG_MAX_OBJECTS];
696 /* These flags could be combined into one byte */
697 exists[MNG_MAX_OBJECTS],
698 frozen[MNG_MAX_OBJECTS],
700 invisible[MNG_MAX_OBJECTS],
701 viewable[MNG_MAX_OBJECTS];
713 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
731 global_x_pixels_per_unit,
732 global_y_pixels_per_unit,
742 global_phys_unit_type,
757 write_png_compression_level,
758 write_png_compression_strategy,
759 write_png_compression_filter,
766 #ifdef MNG_BASI_SUPPORTED
774 basi_compression_method,
776 basi_interlace_method,
799 /* Added at version 6.6.6-7 */
807 /* ping_exclude_iTXt, */
814 ping_exclude_zCCP, /* hex-encoded iCCP */
816 ping_preserve_colormap,
817 /* Added at version 6.8.5-7 */
819 /* Added at version 6.8.9-9 */
826 Forward declarations.
828 static MagickBooleanType
829 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
831 static MagickBooleanType
832 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
834 #if defined(JNG_SUPPORTED)
835 static MagickBooleanType
836 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
839 #if PNG_LIBPNG_VER > 10011
842 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
843 static MagickBooleanType
844 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
846 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
848 * This is true if the high byte and the next highest byte of
849 * each sample of the image, the colormap, and the background color
850 * are equal to each other. We check this by seeing if the samples
851 * are unchanged when we scale them down to 8 and back up to Quantum.
853 * We don't use the method GetImageDepth() because it doesn't check
854 * background and doesn't handle PseudoClass specially.
857 #define QuantumToCharToQuantumEqQuantum(quantum) \
858 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
861 ok_to_reduce=MagickFalse;
863 if (image->depth >= 16)
870 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
871 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
872 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
873 MagickTrue : MagickFalse;
875 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
879 for (indx=0; indx < (ssize_t) image->colors; indx++)
882 QuantumToCharToQuantumEqQuantum(
883 image->colormap[indx].red) &&
884 QuantumToCharToQuantumEqQuantum(
885 image->colormap[indx].green) &&
886 QuantumToCharToQuantumEqQuantum(
887 image->colormap[indx].blue)) ?
888 MagickTrue : MagickFalse;
890 if (ok_to_reduce == MagickFalse)
895 if ((ok_to_reduce != MagickFalse) &&
896 (image->storage_class != PseudoClass))
904 for (y=0; y < (ssize_t) image->rows; y++)
906 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
908 if (p == (const Quantum *) NULL)
910 ok_to_reduce = MagickFalse;
914 for (x=(ssize_t) image->columns-1; x >= 0; x--)
917 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
918 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
919 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
920 MagickTrue : MagickFalse;
922 if (ok_to_reduce == MagickFalse)
925 p+=GetPixelChannels(image);
932 if (ok_to_reduce != MagickFalse)
934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
935 " OK to reduce PNG bit depth to 8 without loss of info");
939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
940 " Not OK to reduce PNG bit depth to 8 without loss of info");
946 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
948 static const char* PngColorTypeToString(const unsigned int color_type)
955 case PNG_COLOR_TYPE_GRAY:
958 case PNG_COLOR_TYPE_GRAY_ALPHA:
959 result = "Gray+Alpha";
961 case PNG_COLOR_TYPE_PALETTE:
964 case PNG_COLOR_TYPE_RGB:
967 case PNG_COLOR_TYPE_RGB_ALPHA:
968 result = "RGB+Alpha";
976 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
980 case PerceptualIntent:
986 case SaturationIntent:
997 static RenderingIntent
998 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1000 switch (ping_intent)
1003 return PerceptualIntent;
1006 return RelativeIntent;
1009 return SaturationIntent;
1012 return AbsoluteIntent;
1015 return UndefinedIntent;
1020 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1022 switch (ping_intent)
1025 return "Perceptual Intent";
1028 return "Relative Intent";
1031 return "Saturation Intent";
1034 return "Absolute Intent";
1037 return "Undefined Intent";
1042 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1044 switch (ping_colortype)
1062 return "UndefinedColorType";
1066 #endif /* PNG_LIBPNG_VER > 10011 */
1067 #endif /* MAGICKCORE_PNG_DELEGATE */
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1080 % IsMNG() returns MagickTrue if the image format type, identified by the
1081 % magick string, is MNG.
1083 % The format of the IsMNG method is:
1085 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1087 % A description of each parameter follows:
1089 % o magick: compare image format pattern against these bytes.
1091 % o length: Specifies the length of the magick string.
1095 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1098 return(MagickFalse);
1100 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1103 return(MagickFalse);
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117 % IsJNG() returns MagickTrue if the image format type, identified by the
1118 % magick string, is JNG.
1120 % The format of the IsJNG method is:
1122 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1124 % A description of each parameter follows:
1126 % o magick: compare image format pattern against these bytes.
1128 % o length: Specifies the length of the magick string.
1132 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1135 return(MagickFalse);
1137 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1140 return(MagickFalse);
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1154 % IsPNG() returns MagickTrue if the image format type, identified by the
1155 % magick string, is PNG.
1157 % The format of the IsPNG method is:
1159 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1161 % A description of each parameter follows:
1163 % o magick: compare image format pattern against these bytes.
1165 % o length: Specifies the length of the magick string.
1168 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1171 return(MagickFalse);
1173 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1176 return(MagickFalse);
1179 #if defined(MAGICKCORE_PNG_DELEGATE)
1180 #if defined(__cplusplus) || defined(c_plusplus)
1184 #if (PNG_LIBPNG_VER > 10011)
1185 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1190 assert(image != (Image *) NULL);
1191 assert(image->signature == MagickSignature);
1192 buffer[0]=(unsigned char) (value >> 24);
1193 buffer[1]=(unsigned char) (value >> 16);
1194 buffer[2]=(unsigned char) (value >> 8);
1195 buffer[3]=(unsigned char) value;
1196 return((size_t) WriteBlob(image,4,buffer));
1199 static void PNGLong(png_bytep p,png_uint_32 value)
1201 *p++=(png_byte) ((value >> 24) & 0xff);
1202 *p++=(png_byte) ((value >> 16) & 0xff);
1203 *p++=(png_byte) ((value >> 8) & 0xff);
1204 *p++=(png_byte) (value & 0xff);
1207 #if defined(JNG_SUPPORTED)
1208 static void PNGsLong(png_bytep p,png_int_32 value)
1210 *p++=(png_byte) ((value >> 24) & 0xff);
1211 *p++=(png_byte) ((value >> 16) & 0xff);
1212 *p++=(png_byte) ((value >> 8) & 0xff);
1213 *p++=(png_byte) (value & 0xff);
1217 static void PNGShort(png_bytep p,png_uint_16 value)
1219 *p++=(png_byte) ((value >> 8) & 0xff);
1220 *p++=(png_byte) (value & 0xff);
1223 static void PNGType(png_bytep p,png_bytep type)
1225 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1228 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1231 if (logging != MagickFalse)
1232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1233 " Writing %c%c%c%c chunk, length: %.20g",
1234 type[0],type[1],type[2],type[3],(double) length);
1236 #endif /* PNG_LIBPNG_VER > 10011 */
1238 #if defined(__cplusplus) || defined(c_plusplus)
1242 #if PNG_LIBPNG_VER > 10011
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248 % R e a d P N G I m a g e %
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1255 % Multiple-image Network Graphics (MNG) image file and returns it. It
1256 % allocates the memory necessary for the new Image structure and returns a
1257 % pointer to the new image or set of images.
1259 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1261 % The format of the ReadPNGImage method is:
1263 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1265 % A description of each parameter follows:
1267 % o image_info: the image info.
1269 % o exception: return any errors or warnings in this structure.
1271 % To do, more or less in chronological order (as of version 5.5.2,
1272 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1274 % Get 16-bit cheap transparency working.
1276 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1278 % Preserve all unknown and not-yet-handled known chunks found in input
1279 % PNG file and copy them into output PNG files according to the PNG
1282 % (At this point, PNG encoding should be in full MNG compliance)
1284 % Provide options for choice of background to use when the MNG BACK
1285 % chunk is not present or is not mandatory (i.e., leave transparent,
1286 % user specified, MNG BACK, PNG bKGD)
1288 % Implement LOOP/ENDL [done, but could do discretionary loops more
1289 % efficiently by linking in the duplicate frames.].
1291 % Decode and act on the MHDR simplicity profile (offer option to reject
1292 % files or attempt to process them anyway when the profile isn't LC or VLC).
1294 % Upgrade to full MNG without Delta-PNG.
1296 % o BACK [done a while ago except for background image ID]
1297 % o MOVE [done 15 May 1999]
1298 % o CLIP [done 15 May 1999]
1299 % o DISC [done 19 May 1999]
1300 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1301 % o SEEK [partially done 19 May 1999 (discard function only)]
1305 % o MNG-level tEXt/iTXt/zTXt
1310 % o iTXt (wait for libpng implementation).
1312 % Use the scene signature to discover when an identical scene is
1313 % being reused, and just point to the original image->exception instead
1314 % of storing another set of pixels. This not specific to MNG
1315 % but could be applied generally.
1317 % Upgrade to full MNG with Delta-PNG.
1319 % JNG tEXt/iTXt/zTXt
1321 % We will not attempt to read files containing the CgBI chunk.
1322 % They are really Xcode files meant for display on the iPhone.
1323 % These are not valid PNG files and it is impossible to recover
1324 % the original PNG from files that have been converted to Xcode-PNG,
1325 % since irretrievable loss of color data has occurred due to the
1326 % use of premultiplied alpha.
1329 #if defined(__cplusplus) || defined(c_plusplus)
1334 This the function that does the actual reading of data. It is
1335 the same as the one supplied in libpng, except that it receives the
1336 datastream from the ReadBlob() function instead of standard input.
1338 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1343 image=(Image *) png_get_io_ptr(png_ptr);
1349 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1350 if (check != length)
1355 (void) FormatLocaleString(msg,MaxTextExtent,
1356 "Expected %.20g bytes; found %.20g bytes",(double) length,
1358 png_warning(png_ptr,msg);
1359 png_error(png_ptr,"Read Exception");
1364 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1365 !defined(PNG_MNG_FEATURES_SUPPORTED)
1366 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1367 * older than libpng-1.0.3a, which was the first to allow the empty
1368 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1369 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1370 * encountered after an empty PLTE, so we have to look ahead for bKGD
1371 * chunks and remove them from the datastream that is passed to libpng,
1372 * and store their contents for later use.
1374 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1389 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1390 image=(Image *) mng_info->image;
1391 while (mng_info->bytes_in_read_buffer && length)
1393 data[i]=mng_info->read_buffer[i];
1394 mng_info->bytes_in_read_buffer--;
1400 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1402 if (check != length)
1403 png_error(png_ptr,"Read Exception");
1407 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1410 check=(png_size_t) ReadBlob(image,(size_t) length,
1411 (char *) mng_info->read_buffer);
1412 mng_info->read_buffer[4]=0;
1413 mng_info->bytes_in_read_buffer=4;
1414 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1415 mng_info->found_empty_plte=MagickTrue;
1416 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1418 mng_info->found_empty_plte=MagickFalse;
1419 mng_info->have_saved_bkgd_index=MagickFalse;
1423 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1426 check=(png_size_t) ReadBlob(image,(size_t) length,
1427 (char *) mng_info->read_buffer);
1428 mng_info->read_buffer[4]=0;
1429 mng_info->bytes_in_read_buffer=4;
1430 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1431 if (mng_info->found_empty_plte)
1434 Skip the bKGD data byte and CRC.
1437 ReadBlob(image,5,(char *) mng_info->read_buffer);
1438 check=(png_size_t) ReadBlob(image,(size_t) length,
1439 (char *) mng_info->read_buffer);
1440 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1441 mng_info->have_saved_bkgd_index=MagickTrue;
1442 mng_info->bytes_in_read_buffer=0;
1450 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1455 image=(Image *) png_get_io_ptr(png_ptr);
1461 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1463 if (check != length)
1464 png_error(png_ptr,"WriteBlob Failed");
1468 static void png_flush_data(png_structp png_ptr)
1473 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1474 static int PalettesAreEqual(Image *a,Image *b)
1479 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1480 return((int) MagickFalse);
1482 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1483 return((int) MagickFalse);
1485 if (a->colors != b->colors)
1486 return((int) MagickFalse);
1488 for (i=0; i < (ssize_t) a->colors; i++)
1490 if ((a->colormap[i].red != b->colormap[i].red) ||
1491 (a->colormap[i].green != b->colormap[i].green) ||
1492 (a->colormap[i].blue != b->colormap[i].blue))
1493 return((int) MagickFalse);
1496 return((int) MagickTrue);
1500 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1502 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1503 mng_info->exists[i] && !mng_info->frozen[i])
1505 #ifdef MNG_OBJECT_BUFFERS
1506 if (mng_info->ob[i] != (MngBuffer *) NULL)
1508 if (mng_info->ob[i]->reference_count > 0)
1509 mng_info->ob[i]->reference_count--;
1511 if (mng_info->ob[i]->reference_count == 0)
1513 if (mng_info->ob[i]->image != (Image *) NULL)
1514 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1516 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1519 mng_info->ob[i]=(MngBuffer *) NULL;
1521 mng_info->exists[i]=MagickFalse;
1522 mng_info->invisible[i]=MagickFalse;
1523 mng_info->viewable[i]=MagickFalse;
1524 mng_info->frozen[i]=MagickFalse;
1525 mng_info->x_off[i]=0;
1526 mng_info->y_off[i]=0;
1527 mng_info->object_clip[i].left=0;
1528 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1529 mng_info->object_clip[i].top=0;
1530 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1534 static void MngInfoFreeStruct(MngInfo *mng_info,
1535 MagickBooleanType *have_mng_structure)
1537 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1542 for (i=1; i < MNG_MAX_OBJECTS; i++)
1543 MngInfoDiscardObject(mng_info,i);
1545 if (mng_info->global_plte != (png_colorp) NULL)
1546 mng_info->global_plte=(png_colorp)
1547 RelinquishMagickMemory(mng_info->global_plte);
1549 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1550 *have_mng_structure=MagickFalse;
1554 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1560 if (box.left < box2.left)
1563 if (box.top < box2.top)
1566 if (box.right > box2.right)
1567 box.right=box2.right;
1569 if (box.bottom > box2.bottom)
1570 box.bottom=box2.bottom;
1575 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1581 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1583 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1584 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1585 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1586 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1587 if (delta_type != 0)
1589 box.left+=previous_box.left;
1590 box.right+=previous_box.right;
1591 box.top+=previous_box.top;
1592 box.bottom+=previous_box.bottom;
1598 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1604 Read two ssize_ts from CLON, MOVE or PAST chunk
1606 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1607 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1609 if (delta_type != 0)
1611 pair.a+=previous_pair.a;
1612 pair.b+=previous_pair.b;
1618 static long mng_get_long(unsigned char *p)
1620 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1623 typedef struct _PNGErrorInfo
1632 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1643 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1644 image=error_info->image;
1645 exception=error_info->exception;
1647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1648 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1650 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1651 "`%s'",image->filename);
1653 #if (PNG_LIBPNG_VER < 10500)
1654 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1655 * are building with libpng-1.4.x and can be ignored.
1657 longjmp(ping->jmpbuf,1);
1659 png_longjmp(ping,1);
1663 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1674 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1675 png_error(ping, message);
1677 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1678 image=error_info->image;
1679 exception=error_info->exception;
1680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1681 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1683 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1684 message,"`%s'",image->filename);
1687 #ifdef PNG_USER_MEM_SUPPORTED
1688 #if PNG_LIBPNG_VER >= 10400
1689 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1691 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1695 return((png_voidp) AcquireMagickMemory((size_t) size));
1699 Free a pointer. It is removed from the list at the same time.
1701 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1704 ptr=RelinquishMagickMemory(ptr);
1705 return((png_free_ptr) NULL);
1709 #if defined(__cplusplus) || defined(c_plusplus)
1714 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1715 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1720 register unsigned char
1734 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1735 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1736 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1737 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,10,11,12,
1742 /* look for newline */
1746 /* look for length */
1747 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1750 length=(png_uint_32) StringToLong(sp);
1752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1753 " length: %lu",(unsigned long) length);
1755 while (*sp != ' ' && *sp != '\n')
1758 /* allocate space */
1761 png_warning(ping,"invalid profile length");
1762 return(MagickFalse);
1765 profile=BlobToStringInfo((const void *) NULL,length);
1767 if (profile == (StringInfo *) NULL)
1769 png_warning(ping, "unable to copy profile");
1770 return(MagickFalse);
1773 /* copy profile, skipping white space and column 1 "=" signs */
1774 dp=GetStringInfoDatum(profile);
1777 for (i=0; i < (ssize_t) nibbles; i++)
1779 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1783 png_warning(ping, "ran out of profile data");
1784 profile=DestroyStringInfo(profile);
1785 return(MagickFalse);
1791 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1794 (*dp++)+=unhex[(int) *sp++];
1797 We have already read "Raw profile type.
1799 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1800 profile=DestroyStringInfo(profile);
1802 if (image_info->verbose)
1803 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1808 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1809 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1815 /* The unknown chunk structure contains the chunk data:
1820 Note that libpng has already taken care of the CRC handling.
1823 LogMagickEvent(CoderEvent,GetMagickModule(),
1824 " read_vpag_chunk: found %c%c%c%c chunk",
1825 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1827 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1828 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1829 return(0); /* Did not recognize */
1831 /* recognized vpAg */
1833 if (chunk->size != 9)
1834 return(-1); /* Error return */
1836 if (chunk->data[8] != 0)
1837 return(0); /* ImageMagick requires pixel units */
1839 image=(Image *) png_get_user_chunk_ptr(ping);
1841 image->page.width=(size_t) ((chunk->data[0] << 24) |
1842 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1844 image->page.height=(size_t) ((chunk->data[4] << 24) |
1845 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1847 /* Return one of the following: */
1848 /* return(-n); chunk had an error */
1849 /* return(0); did not recognize */
1850 /* return(n); success */
1857 #if defined(PNG_tIME_SUPPORTED)
1858 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1859 ExceptionInfo *exception)
1864 if (png_get_tIME(ping,info,&time))
1869 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1870 time->year,time->month,time->day,time->hour,time->minute,time->second);
1871 SetImageProperty(image,"png:tIME",timestamp,exception);
1877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1881 % R e a d O n e P N G I m a g e %
1885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1887 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1888 % (minus the 8-byte signature) and returns it. It allocates the memory
1889 % necessary for the new Image structure and returns a pointer to the new
1892 % The format of the ReadOnePNGImage method is:
1894 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1895 % ExceptionInfo *exception)
1897 % A description of each parameter follows:
1899 % o mng_info: Specifies a pointer to a MngInfo structure.
1901 % o image_info: the image info.
1903 % o exception: return any errors or warnings in this structure.
1906 static Image *ReadOnePNGImage(MngInfo *mng_info,
1907 const ImageInfo *image_info, ExceptionInfo *exception)
1909 /* Read one PNG image */
1911 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1924 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
1934 ping_interlace_method,
1935 ping_compression_method,
1949 ping_found_sRGB_cHRM,
1954 *volatile pixel_info;
1992 register unsigned char
2012 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2013 png_byte unused_chunks[]=
2015 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2016 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2017 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2018 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2019 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2020 #if !defined(PNG_tIME_SUPPORTED)
2021 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2023 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2024 /* ignore the APNG chunks */
2025 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2026 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2027 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2032 /* Define these outside of the following "if logging()" block so they will
2033 * show in debuggers.
2036 (void) ConcatenateMagickString(im_vers,
2037 MagickLibVersionText,32);
2038 (void) ConcatenateMagickString(im_vers,
2039 MagickLibAddendum,32);
2042 (void) ConcatenateMagickString(libpng_vers,
2043 PNG_LIBPNG_VER_STRING,32);
2045 (void) ConcatenateMagickString(libpng_runv,
2046 png_get_libpng_ver(NULL),32);
2049 (void) ConcatenateMagickString(zlib_vers,
2052 (void) ConcatenateMagickString(zlib_runv,
2055 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2056 " Enter ReadOnePNGImage()\n"
2057 " IM version = %s\n"
2058 " Libpng version = %s",
2059 im_vers, libpng_vers);
2061 if (logging != MagickFalse)
2063 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2065 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2068 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2070 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2072 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2077 #if (PNG_LIBPNG_VER < 10200)
2078 if (image_info->verbose)
2079 printf("Your PNG library (libpng-%s) is rather old.\n",
2080 PNG_LIBPNG_VER_STRING);
2083 #if (PNG_LIBPNG_VER >= 10400)
2084 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2085 if (image_info->verbose)
2087 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2088 PNG_LIBPNG_VER_STRING);
2089 printf("Please update it.\n");
2095 quantum_info = (QuantumInfo *) NULL;
2096 image=mng_info->image;
2098 if (logging != MagickFalse)
2100 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2101 " Before reading:\n"
2102 " image->alpha_trait=%d"
2103 " image->rendering_intent=%d\n"
2104 " image->colorspace=%d\n"
2106 (int) image->alpha_trait, (int) image->rendering_intent,
2107 (int) image->colorspace, image->gamma);
2109 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2111 /* Set to an out-of-range color unless tRNS chunk is present */
2112 transparent_color.red=65537;
2113 transparent_color.green=65537;
2114 transparent_color.blue=65537;
2115 transparent_color.alpha=65537;
2120 num_raw_profiles = 0;
2122 ping_found_cHRM = MagickFalse;
2123 ping_found_gAMA = MagickFalse;
2124 ping_found_iCCP = MagickFalse;
2125 ping_found_sRGB = MagickFalse;
2126 ping_found_sRGB_cHRM = MagickFalse;
2127 ping_preserve_iCCP = MagickFalse;
2131 Allocate the PNG structures
2133 #ifdef PNG_USER_MEM_SUPPORTED
2134 error_info.image=image;
2135 error_info.exception=exception;
2136 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2137 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2138 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2140 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2141 MagickPNGErrorHandler,MagickPNGWarningHandler);
2143 if (ping == (png_struct *) NULL)
2144 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2146 ping_info=png_create_info_struct(ping);
2148 if (ping_info == (png_info *) NULL)
2150 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2151 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2154 end_info=png_create_info_struct(ping);
2156 if (end_info == (png_info *) NULL)
2158 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2159 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 pixel_info=(MemoryInfo *) NULL;
2164 if (setjmp(png_jmpbuf(ping)))
2167 PNG image is corrupt.
2169 png_destroy_read_struct(&ping,&ping_info,&end_info);
2171 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2172 UnlockSemaphoreInfo(ping_semaphore);
2175 if (pixel_info != (MemoryInfo *) NULL)
2176 pixel_info=RelinquishVirtualMemory(pixel_info);
2178 if (logging != MagickFalse)
2179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180 " exit ReadOnePNGImage() with error.");
2182 if (image != (Image *) NULL)
2185 return(GetFirstImageInList(image));
2188 /* { For navigation to end of SETJMP-protected block. Within this
2189 * block, use png_error() instead of Throwing an Exception, to ensure
2190 * that libpng is able to clean up, and that the semaphore is unlocked.
2193 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2194 LockSemaphoreInfo(ping_semaphore);
2197 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2198 /* Allow benign errors */
2199 png_set_benign_errors(ping, 1);
2203 Prepare PNG for reading.
2206 mng_info->image_found++;
2207 png_set_sig_bytes(ping,8);
2209 if (LocaleCompare(image_info->magick,"MNG") == 0)
2211 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2212 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2213 png_set_read_fn(ping,image,png_get_data);
2215 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2216 png_permit_empty_plte(ping,MagickTrue);
2217 png_set_read_fn(ping,image,png_get_data);
2219 mng_info->image=image;
2220 mng_info->bytes_in_read_buffer=0;
2221 mng_info->found_empty_plte=MagickFalse;
2222 mng_info->have_saved_bkgd_index=MagickFalse;
2223 png_set_read_fn(ping,mng_info,mng_get_data);
2229 png_set_read_fn(ping,image,png_get_data);
2235 value=GetImageOption(image_info,"profile:skip");
2237 if (IsOptionMember("ICC",value) == MagickFalse)
2240 value=GetImageOption(image_info,"png:preserve-iCCP");
2243 value=GetImageArtifact(image,"png:preserve-iCCP");
2246 ping_preserve_iCCP=MagickTrue;
2248 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2249 /* Don't let libpng check for ICC/sRGB profile because we're going
2250 * to do that anyway. This feature was added at libpng-1.6.12.
2251 * If logging, go ahead and check and issue a warning as appropriate.
2253 if (logging == MagickFalse)
2254 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2257 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2260 png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2264 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2265 /* Ignore unused chunks and all unknown chunks except for vpAg */
2266 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2267 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2269 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2271 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2272 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2273 (int)sizeof(unused_chunks)/5);
2274 /* Callback for other unknown chunks */
2275 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2278 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2279 # if (PNG_LIBPNG_VER >= 10400)
2280 /* Limit the size of the chunk storage cache used for sPLT, text,
2281 * and unknown chunks.
2283 png_set_chunk_cache_max(ping, 32767);
2287 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2288 /* Disable new libpng-1.5.10 feature */
2289 png_set_check_for_invalid_index (ping, 0);
2292 #if (PNG_LIBPNG_VER < 10400)
2293 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2294 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2295 /* Disable thread-unsafe features of pnggccrd */
2296 if (png_access_version_number() >= 10200)
2298 png_uint_32 mmx_disable_mask=0;
2299 png_uint_32 asm_flags;
2301 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2302 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2303 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2304 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2305 asm_flags=png_get_asm_flags(ping);
2306 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2311 png_read_info(ping,ping_info);
2313 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2314 &ping_bit_depth,&ping_color_type,
2315 &ping_interlace_method,&ping_compression_method,
2316 &ping_filter_method);
2318 ping_file_depth = ping_bit_depth;
2320 /* Swap bytes if requested */
2321 if (ping_file_depth == 16)
2326 value=GetImageOption(image_info,"png:swap-bytes");
2329 value=GetImageArtifact(image,"png:swap-bytes");
2335 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2340 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2341 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2343 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2344 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2347 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2350 (void) png_get_bKGD(ping, ping_info, &ping_background);
2352 if (ping_bit_depth < 8)
2354 png_set_packing(ping);
2358 image->depth=ping_bit_depth;
2359 image->depth=GetImageQuantumDepth(image,MagickFalse);
2360 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2362 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2363 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2365 image->rendering_intent=UndefinedIntent;
2366 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2367 (void) ResetMagickMemory(&image->chromaticity,0,
2368 sizeof(image->chromaticity));
2371 if (logging != MagickFalse)
2373 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2374 " PNG width: %.20g, height: %.20g\n"
2375 " PNG color_type: %d, bit_depth: %d\n"
2376 " PNG compression_method: %d\n"
2377 " PNG interlace_method: %d, filter_method: %d",
2378 (double) ping_width, (double) ping_height,
2379 ping_color_type, ping_bit_depth,
2380 ping_compression_method,
2381 ping_interlace_method,ping_filter_method);
2385 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2387 ping_found_iCCP=MagickTrue;
2388 if (logging != MagickFalse)
2389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2390 " Found PNG iCCP chunk.");
2393 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2395 ping_found_gAMA=MagickTrue;
2396 if (logging != MagickFalse)
2397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2398 " Found PNG gAMA chunk.");
2401 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2403 ping_found_cHRM=MagickTrue;
2404 if (logging != MagickFalse)
2405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2406 " Found PNG cHRM chunk.");
2409 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2412 ping_found_sRGB=MagickTrue;
2413 if (logging != MagickFalse)
2414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2415 " Found PNG sRGB chunk.");
2418 #ifdef PNG_READ_iCCP_SUPPORTED
2419 if (ping_found_iCCP !=MagickTrue &&
2420 ping_found_sRGB != MagickTrue &&
2421 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2423 ping_found_iCCP=MagickTrue;
2424 if (logging != MagickFalse)
2425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2426 " Found PNG iCCP chunk.");
2429 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2434 #if (PNG_LIBPNG_VER < 10500)
2448 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2451 if (profile_length != 0)
2456 if (logging != MagickFalse)
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2458 " Reading PNG iCCP chunk.");
2460 profile=BlobToStringInfo(info,profile_length);
2462 if (profile == (StringInfo *) NULL)
2464 png_warning(ping, "ICC profile is NULL");
2465 profile=DestroyStringInfo(profile);
2469 if (ping_preserve_iCCP == MagickFalse)
2483 length=(png_uint_32) GetStringInfoLength(profile);
2485 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2487 if (length == sRGB_info[icheck].len)
2491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2492 " Got a %lu-byte ICC profile (potentially sRGB)",
2493 (unsigned long) length);
2495 data=GetStringInfoDatum(profile);
2496 profile_crc=crc32(0,data,length);
2498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2499 " with crc=%8x",(unsigned int) profile_crc);
2503 if (profile_crc == sRGB_info[icheck].crc)
2505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2506 " It is sRGB with rendering intent = %s",
2507 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2508 sRGB_info[icheck].intent));
2509 if (image->rendering_intent==UndefinedIntent)
2511 image->rendering_intent=
2512 Magick_RenderingIntent_from_PNG_RenderingIntent(
2513 sRGB_info[icheck].intent);
2519 if (sRGB_info[icheck].len == 0)
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522 " Got a %lu-byte ICC profile not recognized as sRGB",
2523 (unsigned long) length);
2524 (void) SetImageProfile(image,"icc",profile,exception);
2527 else /* Preserve-iCCP */
2529 (void) SetImageProfile(image,"icc",profile,exception);
2532 profile=DestroyStringInfo(profile);
2538 #if defined(PNG_READ_sRGB_SUPPORTED)
2540 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2543 if (png_get_sRGB(ping,ping_info,&intent))
2545 if (image->rendering_intent == UndefinedIntent)
2546 image->rendering_intent=
2547 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2549 if (logging != MagickFalse)
2550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2551 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2555 else if (mng_info->have_global_srgb)
2557 if (image->rendering_intent == UndefinedIntent)
2558 image->rendering_intent=
2559 Magick_RenderingIntent_from_PNG_RenderingIntent
2560 (mng_info->global_srgb_intent);
2567 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2568 if (mng_info->have_global_gama)
2569 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2571 if (png_get_gAMA(ping,ping_info,&file_gamma))
2573 image->gamma=(float) file_gamma;
2574 if (logging != MagickFalse)
2575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2576 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2580 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2582 if (mng_info->have_global_chrm != MagickFalse)
2584 (void) png_set_cHRM(ping,ping_info,
2585 mng_info->global_chrm.white_point.x,
2586 mng_info->global_chrm.white_point.y,
2587 mng_info->global_chrm.red_primary.x,
2588 mng_info->global_chrm.red_primary.y,
2589 mng_info->global_chrm.green_primary.x,
2590 mng_info->global_chrm.green_primary.y,
2591 mng_info->global_chrm.blue_primary.x,
2592 mng_info->global_chrm.blue_primary.y);
2596 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2598 (void) png_get_cHRM(ping,ping_info,
2599 &image->chromaticity.white_point.x,
2600 &image->chromaticity.white_point.y,
2601 &image->chromaticity.red_primary.x,
2602 &image->chromaticity.red_primary.y,
2603 &image->chromaticity.green_primary.x,
2604 &image->chromaticity.green_primary.y,
2605 &image->chromaticity.blue_primary.x,
2606 &image->chromaticity.blue_primary.y);
2608 ping_found_cHRM=MagickTrue;
2610 if (image->chromaticity.red_primary.x>0.6399f &&
2611 image->chromaticity.red_primary.x<0.6401f &&
2612 image->chromaticity.red_primary.y>0.3299f &&
2613 image->chromaticity.red_primary.y<0.3301f &&
2614 image->chromaticity.green_primary.x>0.2999f &&
2615 image->chromaticity.green_primary.x<0.3001f &&
2616 image->chromaticity.green_primary.y>0.5999f &&
2617 image->chromaticity.green_primary.y<0.6001f &&
2618 image->chromaticity.blue_primary.x>0.1499f &&
2619 image->chromaticity.blue_primary.x<0.1501f &&
2620 image->chromaticity.blue_primary.y>0.0599f &&
2621 image->chromaticity.blue_primary.y<0.0601f &&
2622 image->chromaticity.white_point.x>0.3126f &&
2623 image->chromaticity.white_point.x<0.3128f &&
2624 image->chromaticity.white_point.y>0.3289f &&
2625 image->chromaticity.white_point.y<0.3291f)
2626 ping_found_sRGB_cHRM=MagickTrue;
2629 if (image->rendering_intent != UndefinedIntent)
2631 if (ping_found_sRGB != MagickTrue &&
2632 (ping_found_gAMA != MagickTrue ||
2633 (image->gamma > .45 && image->gamma < .46)) &&
2634 (ping_found_cHRM != MagickTrue ||
2635 ping_found_sRGB_cHRM != MagickFalse) &&
2636 ping_found_iCCP != MagickTrue)
2638 png_set_sRGB(ping,ping_info,
2639 Magick_RenderingIntent_to_PNG_RenderingIntent
2640 (image->rendering_intent));
2641 file_gamma=1.000f/2.200f;
2642 ping_found_sRGB=MagickTrue;
2643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2644 " Setting sRGB as if in input");
2648 #if defined(PNG_oFFs_SUPPORTED)
2649 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2651 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2652 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2654 if (logging != MagickFalse)
2655 if (image->page.x || image->page.y)
2656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2658 image->page.x,(double) image->page.y);
2661 #if defined(PNG_pHYs_SUPPORTED)
2662 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2664 if (mng_info->have_global_phys)
2666 png_set_pHYs(ping,ping_info,
2667 mng_info->global_x_pixels_per_unit,
2668 mng_info->global_y_pixels_per_unit,
2669 mng_info->global_phys_unit_type);
2676 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2679 Set image resolution.
2681 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2683 image->resolution.x=(double) x_resolution;
2684 image->resolution.y=(double) y_resolution;
2686 if (unit_type == PNG_RESOLUTION_METER)
2688 image->units=PixelsPerCentimeterResolution;
2689 image->resolution.x=(double) x_resolution/100.0;
2690 image->resolution.y=(double) y_resolution/100.0;
2693 if (logging != MagickFalse)
2694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2695 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2696 (double) x_resolution,(double) y_resolution,unit_type);
2700 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2705 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2707 if ((number_colors == 0) &&
2708 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2710 if (mng_info->global_plte_length)
2712 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2713 (int) mng_info->global_plte_length);
2715 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2717 if (mng_info->global_trns_length)
2720 "global tRNS has more entries than global PLTE");
2724 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2725 (int) mng_info->global_trns_length,NULL);
2728 #ifdef PNG_READ_bKGD_SUPPORTED
2730 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2731 mng_info->have_saved_bkgd_index ||
2733 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2738 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2739 if (mng_info->have_saved_bkgd_index)
2740 background.index=mng_info->saved_bkgd_index;
2742 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2743 background.index=ping_background->index;
2745 background.red=(png_uint_16)
2746 mng_info->global_plte[background.index].red;
2748 background.green=(png_uint_16)
2749 mng_info->global_plte[background.index].green;
2751 background.blue=(png_uint_16)
2752 mng_info->global_plte[background.index].blue;
2754 background.gray=(png_uint_16)
2755 mng_info->global_plte[background.index].green;
2757 png_set_bKGD(ping,ping_info,&background);
2762 png_error(ping,"No global PLTE in file");
2766 #ifdef PNG_READ_bKGD_SUPPORTED
2767 if (mng_info->have_global_bkgd &&
2768 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2769 image->background_color=mng_info->mng_global_bkgd;
2771 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2776 /* Set image background color.
2777 * Scale background components to 16-bit, then scale
2783 if (ping_file_depth == 1)
2786 else if (ping_file_depth == 2)
2789 else if (ping_file_depth == 4)
2792 if (ping_file_depth <= 8)
2795 ping_background->red *= bkgd_scale;
2796 ping_background->green *= bkgd_scale;
2797 ping_background->blue *= bkgd_scale;
2799 if (logging != MagickFalse)
2801 if (logging != MagickFalse)
2802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2803 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2804 " bkgd_scale=%d. ping_background=(%d,%d,%d).",
2805 ping_background->red,ping_background->green,
2806 ping_background->blue,
2807 bkgd_scale,ping_background->red,
2808 ping_background->green,ping_background->blue);
2811 image->background_color.red=
2812 ScaleShortToQuantum(ping_background->red);
2814 image->background_color.green=
2815 ScaleShortToQuantum(ping_background->green);
2817 image->background_color.blue=
2818 ScaleShortToQuantum(ping_background->blue);
2820 image->background_color.alpha=OpaqueAlpha;
2822 if (logging != MagickFalse)
2823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2824 " image->background_color=(%.20g,%.20g,%.20g).",
2825 (double) image->background_color.red,
2826 (double) image->background_color.green,
2827 (double) image->background_color.blue);
2829 #endif /* PNG_READ_bKGD_SUPPORTED */
2831 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2834 Image has a tRNS chunk.
2842 if (logging != MagickFalse)
2843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2844 " Reading PNG tRNS chunk.");
2846 max_sample = (int) ((one << ping_file_depth) - 1);
2848 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2849 (int)ping_trans_color->gray > max_sample) ||
2850 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2851 ((int)ping_trans_color->red > max_sample ||
2852 (int)ping_trans_color->green > max_sample ||
2853 (int)ping_trans_color->blue > max_sample)))
2855 if (logging != MagickFalse)
2856 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2857 " Ignoring PNG tRNS chunk with out-of-range sample.");
2858 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2859 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2860 image->alpha_trait=UndefinedPixelTrait;
2867 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2869 /* Scale transparent_color to short */
2870 transparent_color.red= scale_to_short*ping_trans_color->red;
2871 transparent_color.green= scale_to_short*ping_trans_color->green;
2872 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2873 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2875 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2877 if (logging != MagickFalse)
2879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2880 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
2881 (int) ping_trans_color->gray,(int) transparent_color.alpha);
2884 transparent_color.red=transparent_color.alpha;
2885 transparent_color.green=transparent_color.alpha;
2886 transparent_color.blue=transparent_color.alpha;
2890 #if defined(PNG_READ_sBIT_SUPPORTED)
2891 if (mng_info->have_global_sbit)
2893 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2894 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2897 num_passes=png_set_interlace_handling(ping);
2899 png_read_update_info(ping,ping_info);
2901 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2904 Initialize image structure.
2906 mng_info->image_box.left=0;
2907 mng_info->image_box.right=(ssize_t) ping_width;
2908 mng_info->image_box.top=0;
2909 mng_info->image_box.bottom=(ssize_t) ping_height;
2910 if (mng_info->mng_type == 0)
2912 mng_info->mng_width=ping_width;
2913 mng_info->mng_height=ping_height;
2914 mng_info->frame=mng_info->image_box;
2915 mng_info->clip=mng_info->image_box;
2920 image->page.y=mng_info->y_off[mng_info->object_id];
2923 image->compression=ZipCompression;
2924 image->columns=ping_width;
2925 image->rows=ping_height;
2927 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2928 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2931 image_gamma = image->gamma;
2933 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2934 " image->gamma=%f",(float) image_gamma);
2936 if (image_gamma > 0.75)
2938 /* Set image->rendering_intent to Undefined,
2939 * image->colorspace to GRAY, and reset image->chromaticity.
2941 image->intensity = Rec709LuminancePixelIntensityMethod;
2942 SetImageColorspace(image,GRAYColorspace,exception);
2947 save_rendering_intent = image->rendering_intent;
2949 save_chromaticity = image->chromaticity;
2951 SetImageColorspace(image,GRAYColorspace,exception);
2952 image->rendering_intent = save_rendering_intent;
2953 image->chromaticity = save_chromaticity;
2956 image->gamma = image_gamma;
2959 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2960 " image->colorspace=%d",(int) image->colorspace);
2962 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2963 ((int) ping_bit_depth < 16 &&
2964 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2969 image->storage_class=PseudoClass;
2971 image->colors=one << ping_file_depth;
2972 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2973 if (image->colors > 256)
2976 if (image->colors > 65536L)
2977 image->colors=65536L;
2979 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2984 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2985 image->colors=(size_t) number_colors;
2987 if (logging != MagickFalse)
2988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2989 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2993 if (image->storage_class == PseudoClass)
2996 Initialize image colormap.
2998 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
2999 png_error(ping,"Memory allocation failed");
3001 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3006 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3008 for (i=0; i < (ssize_t) number_colors; i++)
3010 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3011 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3012 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3015 for ( ; i < (ssize_t) image->colors; i++)
3017 image->colormap[i].red=0;
3018 image->colormap[i].green=0;
3019 image->colormap[i].blue=0;
3028 scale = (Quantum) (65535UL)/((1UL << ping_file_depth)-1);
3030 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3031 scale = ScaleShortToQuantum(scale);
3034 for (i=0; i < (ssize_t) image->colors; i++)
3036 image->colormap[i].red=(Quantum) (i*scale);
3037 image->colormap[i].green=(Quantum) (i*scale);
3038 image->colormap[i].blue=(Quantum) (i*scale);
3043 /* Set some properties for reporting by "identify" */
3048 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3049 ping_interlace_method in value */
3051 (void) FormatLocaleString(msg,MaxTextExtent,
3052 "%d, %d",(int) ping_width, (int) ping_height);
3053 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3055 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3056 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3058 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3059 (int) ping_color_type,
3060 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3061 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3063 if (ping_interlace_method == 0)
3065 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3066 (int) ping_interlace_method);
3068 else if (ping_interlace_method == 1)
3070 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3071 (int) ping_interlace_method);
3075 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3076 (int) ping_interlace_method);
3078 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3080 if (number_colors != 0)
3082 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3083 (int) number_colors);
3084 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3088 #if defined(PNG_tIME_SUPPORTED)
3089 read_tIME_chunk(image,ping,ping_info,exception);
3094 Read image scanlines.
3096 if (image->delay != 0)
3097 mng_info->scenes_found++;
3099 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3100 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3101 (image_info->first_scene+image_info->number_scenes))))
3103 /* This happens later in non-ping decodes */
3104 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3105 image->storage_class=DirectClass;
3107 if (logging != MagickFalse)
3108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3109 " Skipping PNG image data for scene %.20g",(double)
3110 mng_info->scenes_found-1);
3111 png_destroy_read_struct(&ping,&ping_info,&end_info);
3113 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3114 UnlockSemaphoreInfo(ping_semaphore);
3117 if (logging != MagickFalse)
3118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3119 " exit ReadOnePNGImage().");
3124 if (logging != MagickFalse)
3125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3126 " Reading PNG IDAT chunk(s)");
3129 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3130 sizeof(*ping_pixels));
3132 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3134 if (pixel_info == (MemoryInfo *) NULL)
3135 png_error(ping,"Memory allocation failed");
3136 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3138 if (logging != MagickFalse)
3139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3140 " Converting PNG pixels to pixel packets");
3142 Convert PNG pixels to pixel packets.
3144 quantum_info=AcquireQuantumInfo(image_info,image);
3146 if (quantum_info == (QuantumInfo *) NULL)
3147 png_error(ping,"Failed to allocate quantum_info");
3149 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3154 found_transparent_pixel;
3156 found_transparent_pixel=MagickFalse;
3158 if (image->storage_class == DirectClass)
3160 for (pass=0; pass < num_passes; pass++)
3163 Convert image to DirectClass pixel packets.
3166 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3167 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3168 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3169 BlendPixelTrait : UndefinedPixelTrait;
3171 for (y=0; y < (ssize_t) image->rows; y++)
3174 row_offset=ping_rowbytes*y;
3179 png_read_row(ping,ping_pixels+row_offset,NULL);
3181 if (pass < num_passes-1)
3184 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3186 if (q == (Quantum *) NULL)
3189 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3190 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3191 GrayQuantum,ping_pixels+row_offset,exception);
3193 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3194 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3195 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3197 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3198 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3199 RGBAQuantum,ping_pixels+row_offset,exception);
3201 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3202 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3203 IndexQuantum,ping_pixels+row_offset,exception);
3205 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3206 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3207 RGBQuantum,ping_pixels+row_offset,exception);
3209 if (found_transparent_pixel == MagickFalse)
3211 /* Is there a transparent pixel in the row? */
3212 if (y== 0 && logging != MagickFalse)
3213 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3214 " Looking for cheap transparent pixel");
3216 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3218 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3219 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3220 (GetPixelAlpha(image,q) != OpaqueAlpha))
3222 if (logging != MagickFalse)
3223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3226 found_transparent_pixel = MagickTrue;
3229 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3230 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3231 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3232 transparent_color.red &&
3233 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3234 transparent_color.green &&
3235 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3236 transparent_color.blue))
3238 if (logging != MagickFalse)
3239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3241 found_transparent_pixel = MagickTrue;
3244 q+=GetPixelChannels(image);
3248 if (num_passes == 1)
3250 status=SetImageProgress(image,LoadImageTag,
3251 (MagickOffsetType) y, image->rows);
3253 if (status == MagickFalse)
3256 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3260 if (num_passes != 1)
3262 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3263 if (status == MagickFalse)
3269 else /* image->storage_class != DirectClass */
3271 for (pass=0; pass < num_passes; pass++)
3280 Convert grayscale image to PseudoClass pixel packets.
3282 if (logging != MagickFalse)
3283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3284 " Converting grayscale pixels to pixel packets");
3286 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3287 BlendPixelTrait : UndefinedPixelTrait;
3289 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3290 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3291 sizeof(*quantum_scanline));
3293 if (quantum_scanline == (Quantum *) NULL)
3294 png_error(ping,"Memory allocation failed");
3296 for (y=0; y < (ssize_t) image->rows; y++)
3302 row_offset=ping_rowbytes*y;
3307 png_read_row(ping,ping_pixels+row_offset,NULL);
3309 if (pass < num_passes-1)
3312 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3314 if (q == (Quantum *) NULL)
3317 p=ping_pixels+row_offset;
3320 switch (ping_bit_depth)
3325 if (ping_color_type == 4)
3326 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3330 alpha=ScaleCharToQuantum((unsigned char)*p++);
3332 SetPixelAlpha(image,alpha,q);
3334 if (alpha != OpaqueAlpha)
3335 found_transparent_pixel = MagickTrue;
3337 q+=GetPixelChannels(image);
3341 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3349 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3351 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3355 if (image->colors > 256)
3356 quantum=((*p++) << 8);
3362 *r=ScaleShortToQuantum(quantum);
3365 if (ping_color_type == 4)
3367 if (image->colors > 256)
3368 quantum=((*p++) << 8);
3374 alpha=ScaleShortToQuantum(quantum);
3375 SetPixelAlpha(image,alpha,q);
3377 if (alpha != OpaqueAlpha)
3378 found_transparent_pixel = MagickTrue;
3380 q+=GetPixelChannels(image);
3383 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3385 p++; /* strip low byte */
3387 if (ping_color_type == 4)
3389 SetPixelAlpha(image,*p++,q);
3391 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3392 found_transparent_pixel = MagickTrue;
3395 q+=GetPixelChannels(image);
3408 Transfer image scanline.
3412 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3414 if (q == (Quantum *) NULL)
3416 for (x=0; x < (ssize_t) image->columns; x++)
3418 SetPixelIndex(image,*r++,q);
3419 q+=GetPixelChannels(image);
3422 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3425 if (num_passes == 1)
3427 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3430 if (status == MagickFalse)
3435 if (num_passes != 1)
3437 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3439 if (status == MagickFalse)
3443 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3446 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3447 UndefinedPixelTrait;
3449 if (logging != MagickFalse)
3451 if (found_transparent_pixel != MagickFalse)
3452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3453 " Found transparent pixel");
3456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3457 " No transparent pixel was found");
3459 ping_color_type&=0x03;
3464 if (quantum_info != (QuantumInfo *) NULL)
3465 quantum_info=DestroyQuantumInfo(quantum_info);
3467 if (image->storage_class == PseudoClass)
3472 alpha_trait=image->alpha_trait;
3473 image->alpha_trait=UndefinedPixelTrait;
3474 (void) SyncImage(image,exception);
3475 image->alpha_trait=alpha_trait;
3478 png_read_end(ping,end_info);
3480 if (logging != MagickFalse)
3482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3483 " image->storage_class=%d\n",(int) image->storage_class);
3486 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3487 (ssize_t) image_info->first_scene && image->delay != 0)
3489 png_destroy_read_struct(&ping,&ping_info,&end_info);
3490 pixel_info=RelinquishVirtualMemory(pixel_info);
3492 (void) SetImageBackgroundColor(image,exception);
3493 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3494 UnlockSemaphoreInfo(ping_semaphore);
3496 if (logging != MagickFalse)
3497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3498 " exit ReadOnePNGImage() early.");
3502 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3508 Image has a transparent background.
3510 storage_class=image->storage_class;
3511 image->alpha_trait=BlendPixelTrait;
3513 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3515 if (storage_class == PseudoClass)
3517 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3519 for (x=0; x < ping_num_trans; x++)
3521 image->colormap[x].alpha_trait=BlendPixelTrait;
3522 image->colormap[x].alpha =
3523 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3527 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3529 for (x=0; x < (int) image->colors; x++)
3531 if (ScaleQuantumToShort(image->colormap[x].red) ==
3532 transparent_color.alpha)
3534 image->colormap[x].alpha_trait=BlendPixelTrait;
3535 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3539 (void) SyncImage(image,exception);
3542 #if 1 /* Should have already been done above, but glennrp problem P10
3547 for (y=0; y < (ssize_t) image->rows; y++)
3549 image->storage_class=storage_class;
3550 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3552 if (q == (Quantum *) NULL)
3556 /* Caution: on a Q8 build, this does not distinguish between
3557 * 16-bit colors that differ only in the low byte
3559 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3561 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3562 transparent_color.red &&
3563 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3564 transparent_color.green &&
3565 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3566 transparent_color.blue)
3568 SetPixelAlpha(image,TransparentAlpha,q);
3571 #if 0 /* I have not found a case where this is needed. */
3574 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3578 q+=GetPixelChannels(image);
3581 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3587 image->storage_class=DirectClass;
3590 for (j = 0; j < 2; j++)
3593 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3594 MagickTrue : MagickFalse;
3596 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3597 MagickTrue : MagickFalse;
3599 if (status != MagickFalse)
3600 for (i=0; i < (ssize_t) num_text; i++)
3602 /* Check for a profile */
3604 if (logging != MagickFalse)
3605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3606 " Reading PNG text chunk");
3608 if (strlen(text[i].key) > 16 &&
3609 memcmp(text[i].key, "Raw profile type ",17) == 0)
3614 value=GetImageOption(image_info,"profile:skip");
3616 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3618 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3621 if (logging != MagickFalse)
3622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3623 " Read raw profile %s",text[i].key+17);
3627 if (logging != MagickFalse)
3628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3629 " Skipping raw profile %s",text[i].key+17);
3638 length=text[i].text_length;
3639 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3641 if (value == (char *) NULL)
3643 png_error(ping,"Memory allocation failed");
3647 (void) ConcatenateMagickString(value,text[i].text,length+2);
3649 /* Don't save "density" or "units" property if we have a pHYs
3652 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3653 (LocaleCompare(text[i].key,"density") != 0 &&
3654 LocaleCompare(text[i].key,"units") != 0))
3655 (void) SetImageProperty(image,text[i].key,value,exception);
3657 if (logging != MagickFalse)
3659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3662 (unsigned long) length,
3666 value=DestroyString(value);
3669 num_text_total += num_text;
3672 #ifdef MNG_OBJECT_BUFFERS
3674 Store the object if necessary.
3676 if (object_id && !mng_info->frozen[object_id])
3678 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3681 create a new object buffer.
3683 mng_info->ob[object_id]=(MngBuffer *)
3684 AcquireMagickMemory(sizeof(MngBuffer));
3686 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3688 mng_info->ob[object_id]->image=(Image *) NULL;
3689 mng_info->ob[object_id]->reference_count=1;
3693 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3694 mng_info->ob[object_id]->frozen)
3696 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3697 png_error(ping,"Memory allocation failed");
3699 if (mng_info->ob[object_id]->frozen)
3700 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3706 if (mng_info->ob[object_id]->image != (Image *) NULL)
3707 mng_info->ob[object_id]->image=DestroyImage
3708 (mng_info->ob[object_id]->image);
3710 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3713 if (mng_info->ob[object_id]->image != (Image *) NULL)
3714 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3717 png_error(ping, "Cloning image for object buffer failed");
3719 if (ping_width > 250000L || ping_height > 250000L)
3720 png_error(ping,"PNG Image dimensions are too large.");
3722 mng_info->ob[object_id]->width=ping_width;
3723 mng_info->ob[object_id]->height=ping_height;
3724 mng_info->ob[object_id]->color_type=ping_color_type;
3725 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3726 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3727 mng_info->ob[object_id]->compression_method=
3728 ping_compression_method;
3729 mng_info->ob[object_id]->filter_method=ping_filter_method;
3731 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3737 Copy the PLTE to the object buffer.
3739 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3740 mng_info->ob[object_id]->plte_length=number_colors;
3742 for (i=0; i < number_colors; i++)
3744 mng_info->ob[object_id]->plte[i]=plte[i];
3749 mng_info->ob[object_id]->plte_length=0;
3754 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3755 * alpha or if a valid tRNS chunk is present, no matter whether there
3756 * is actual transparency present.
3758 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3759 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3760 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3761 BlendPixelTrait : UndefinedPixelTrait;
3763 #if 0 /* I'm not sure what's wrong here but it does not work. */
3764 if (image->alpha_trait != UndefinedPixelTrait)
3766 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3767 (void) SetImageType(image,GrayscaleAlphaType,exception);
3769 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3770 (void) SetImageType(image,PaletteAlphaType,exception);
3773 (void) SetImageType(image,TrueColorAlphaType,exception);
3778 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3779 (void) SetImageType(image,GrayscaleType,exception);
3781 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3782 (void) SetImageType(image,PaletteType,exception);
3785 (void) SetImageType(image,TrueColorType,exception);
3789 /* Set more properties for identify to retrieve */
3794 if (num_text_total != 0)
3796 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3797 (void) FormatLocaleString(msg,MaxTextExtent,
3798 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3799 (void) SetImageProperty(image,"png:text",msg,
3803 if (num_raw_profiles != 0)
3805 (void) FormatLocaleString(msg,MaxTextExtent,
3806 "%d were found", num_raw_profiles);
3807 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3811 if (ping_found_cHRM != MagickFalse)
3813 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3814 "chunk was found (see Chromaticity, above)");
3815 (void) SetImageProperty(image,"png:cHRM",msg,
3819 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3821 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3822 "chunk was found (see Background color, above)");
3823 (void) SetImageProperty(image,"png:bKGD",msg,
3827 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3830 #if defined(PNG_iCCP_SUPPORTED)
3831 if (ping_found_iCCP != MagickFalse)
3832 (void) SetImageProperty(image,"png:iCCP",msg,
3836 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3837 (void) SetImageProperty(image,"png:tRNS",msg,
3840 #if defined(PNG_sRGB_SUPPORTED)
3841 if (ping_found_sRGB != MagickFalse)
3843 (void) FormatLocaleString(msg,MaxTextExtent,
3846 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3847 (void) SetImageProperty(image,"png:sRGB",msg,
3852 if (ping_found_gAMA != MagickFalse)
3854 (void) FormatLocaleString(msg,MaxTextExtent,
3855 "gamma=%.8g (See Gamma, above)",
3857 (void) SetImageProperty(image,"png:gAMA",msg,
3861 #if defined(PNG_pHYs_SUPPORTED)
3862 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3864 (void) FormatLocaleString(msg,MaxTextExtent,
3865 "x_res=%.10g, y_res=%.10g, units=%d",
3866 (double) x_resolution,(double) y_resolution, unit_type);
3867 (void) SetImageProperty(image,"png:pHYs",msg,
3872 #if defined(PNG_oFFs_SUPPORTED)
3873 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3875 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3876 (double) image->page.x,(double) image->page.y);
3877 (void) SetImageProperty(image,"png:oFFs",msg,
3882 #if defined(PNG_tIME_SUPPORTED)
3883 read_tIME_chunk(image,ping,end_info,exception);
3886 if ((image->page.width != 0 && image->page.width != image->columns) ||
3887 (image->page.height != 0 && image->page.height != image->rows))
3889 (void) FormatLocaleString(msg,MaxTextExtent,
3890 "width=%.20g, height=%.20g",
3891 (double) image->page.width,(double) image->page.height);
3892 (void) SetImageProperty(image,"png:vpAg",msg,
3898 Relinquish resources.
3900 png_destroy_read_struct(&ping,&ping_info,&end_info);
3902 pixel_info=RelinquishVirtualMemory(pixel_info);
3904 if (logging != MagickFalse)
3905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3906 " exit ReadOnePNGImage()");
3908 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3909 UnlockSemaphoreInfo(ping_semaphore);
3912 /* } for navigation to beginning of SETJMP-protected block, revert to
3913 * Throwing an Exception when an error occurs.
3918 /* end of reading one PNG image */
3921 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3935 magic_number[MaxTextExtent];
3943 assert(image_info != (const ImageInfo *) NULL);
3944 assert(image_info->signature == MagickSignature);
3946 if (image_info->debug != MagickFalse)
3947 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3948 image_info->filename);
3950 assert(exception != (ExceptionInfo *) NULL);
3951 assert(exception->signature == MagickSignature);
3952 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3953 image=AcquireImage(image_info,exception);
3954 mng_info=(MngInfo *) NULL;
3955 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3957 if (status == MagickFalse)
3958 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3961 Verify PNG signature.
3963 count=ReadBlob(image,8,(unsigned char *) magic_number);
3965 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3966 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3969 Allocate a MngInfo structure.
3971 have_mng_structure=MagickFalse;
3972 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3974 if (mng_info == (MngInfo *) NULL)
3975 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3978 Initialize members of the MngInfo structure.
3980 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3981 mng_info->image=image;
3982 have_mng_structure=MagickTrue;
3984 image=ReadOnePNGImage(mng_info,image_info,exception);
3985 MngInfoFreeStruct(mng_info,&have_mng_structure);
3987 if (image == (Image *) NULL)
3989 if (logging != MagickFalse)
3990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3991 "exit ReadPNGImage() with error");
3993 return((Image *) NULL);
3996 (void) CloseBlob(image);
3998 if ((image->columns == 0) || (image->rows == 0))
4000 if (logging != MagickFalse)
4001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 "exit ReadPNGImage() with error.");
4004 ThrowReaderException(CorruptImageError,"CorruptImage");
4007 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4008 ((image->gamma < .45) || (image->gamma > .46)) &&
4009 !(image->chromaticity.red_primary.x>0.6399f &&
4010 image->chromaticity.red_primary.x<0.6401f &&
4011 image->chromaticity.red_primary.y>0.3299f &&
4012 image->chromaticity.red_primary.y<0.3301f &&
4013 image->chromaticity.green_primary.x>0.2999f &&
4014 image->chromaticity.green_primary.x<0.3001f &&
4015 image->chromaticity.green_primary.y>0.5999f &&
4016 image->chromaticity.green_primary.y<0.6001f &&
4017 image->chromaticity.blue_primary.x>0.1499f &&
4018 image->chromaticity.blue_primary.x<0.1501f &&
4019 image->chromaticity.blue_primary.y>0.0599f &&
4020 image->chromaticity.blue_primary.y<0.0601f &&
4021 image->chromaticity.white_point.x>0.3126f &&
4022 image->chromaticity.white_point.x<0.3128f &&
4023 image->chromaticity.white_point.y>0.3289f &&
4024 image->chromaticity.white_point.y<0.3291f))
4026 SetImageColorspace(image,RGBColorspace,exception);
4029 if (logging != MagickFalse)
4031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4032 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4033 (double) image->page.width,(double) image->page.height,
4034 (double) image->page.x,(double) image->page.y);
4035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4036 " image->colorspace: %d", (int) image->colorspace);
4039 if (logging != MagickFalse)
4040 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4047 #if defined(JNG_SUPPORTED)
4049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4053 % R e a d O n e J N G I m a g e %
4057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4059 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4060 % (minus the 8-byte signature) and returns it. It allocates the memory
4061 % necessary for the new Image structure and returns a pointer to the new
4064 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4066 % The format of the ReadOneJNGImage method is:
4068 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4069 % ExceptionInfo *exception)
4071 % A description of each parameter follows:
4073 % o mng_info: Specifies a pointer to a MngInfo structure.
4075 % o image_info: the image info.
4077 % o exception: return any errors or warnings in this structure.
4080 static Image *ReadOneJNGImage(MngInfo *mng_info,
4081 const ImageInfo *image_info, ExceptionInfo *exception)
4108 jng_image_sample_depth,
4109 jng_image_compression_method,
4110 jng_image_interlace_method,
4111 jng_alpha_sample_depth,
4112 jng_alpha_compression_method,
4113 jng_alpha_filter_method,
4114 jng_alpha_interlace_method;
4116 register const Quantum
4126 register unsigned char
4136 jng_alpha_compression_method=0;
4137 jng_alpha_sample_depth=8;
4141 alpha_image=(Image *) NULL;
4142 color_image=(Image *) NULL;
4143 alpha_image_info=(ImageInfo *) NULL;
4144 color_image_info=(ImageInfo *) NULL;
4146 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4147 " Enter ReadOneJNGImage()");
4149 image=mng_info->image;
4151 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4154 Allocate next image structure.
4156 if (logging != MagickFalse)
4157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4158 " AcquireNextImage()");
4160 AcquireNextImage(image_info,image,exception);
4162 if (GetNextImageInList(image) == (Image *) NULL)
4163 return((Image *) NULL);
4165 image=SyncNextImageInList(image);
4167 mng_info->image=image;
4170 Signature bytes have already been read.
4173 read_JSEP=MagickFalse;
4174 reading_idat=MagickFalse;
4178 type[MaxTextExtent];
4187 Read a new JNG chunk.
4189 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4190 2*GetBlobSize(image));
4192 if (status == MagickFalse)
4196 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4197 length=ReadBlobMSBLong(image);
4198 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4200 if (logging != MagickFalse)
4201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4202 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4203 type[0],type[1],type[2],type[3],(double) length);
4205 if (length > PNG_UINT_31_MAX || count == 0)
4206 ThrowReaderException(CorruptImageError,"CorruptImage");
4209 chunk=(unsigned char *) NULL;
4213 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4215 if (chunk == (unsigned char *) NULL)
4216 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4218 for (i=0; i < (ssize_t) length; i++)
4219 chunk[i]=(unsigned char) ReadBlobByte(image);
4224 (void) ReadBlobMSBLong(image); /* read crc word */
4226 if (memcmp(type,mng_JHDR,4) == 0)
4230 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4231 (p[2] << 8) | p[3]);
4232 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4233 (p[6] << 8) | p[7]);
4234 if ((jng_width == 0) || (jng_height == 0))
4235 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4236 jng_color_type=p[8];
4237 jng_image_sample_depth=p[9];
4238 jng_image_compression_method=p[10];
4239 jng_image_interlace_method=p[11];
4241 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4244 jng_alpha_sample_depth=p[12];
4245 jng_alpha_compression_method=p[13];
4246 jng_alpha_filter_method=p[14];
4247 jng_alpha_interlace_method=p[15];
4249 if (logging != MagickFalse)
4251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4252 " jng_width: %16lu, jng_height: %16lu\n"
4253 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4254 " jng_image_compression_method:%3d",
4255 (unsigned long) jng_width, (unsigned long) jng_height,
4256 jng_color_type, jng_image_sample_depth,
4257 jng_image_compression_method);
4259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4260 " jng_image_interlace_method: %3d"
4261 " jng_alpha_sample_depth: %3d",
4262 jng_image_interlace_method,
4263 jng_alpha_sample_depth);
4265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4266 " jng_alpha_compression_method:%3d\n"
4267 " jng_alpha_filter_method: %3d\n"
4268 " jng_alpha_interlace_method: %3d",
4269 jng_alpha_compression_method,
4270 jng_alpha_filter_method,
4271 jng_alpha_interlace_method);
4276 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4282 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4283 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4284 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4287 o create color_image
4288 o open color_blob, attached to color_image
4289 o if (color type has alpha)
4290 open alpha_blob, attached to alpha_image
4293 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4295 if (color_image_info == (ImageInfo *) NULL)
4296 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4298 GetImageInfo(color_image_info);
4299 color_image=AcquireImage(color_image_info,exception);
4301 if (color_image == (Image *) NULL)
4302 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4304 if (logging != MagickFalse)
4305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4306 " Creating color_blob.");
4308 (void) AcquireUniqueFilename(color_image->filename);
4309 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4312 if (status == MagickFalse)
4313 return((Image *) NULL);
4315 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4317 alpha_image_info=(ImageInfo *)
4318 AcquireMagickMemory(sizeof(ImageInfo));
4320 if (alpha_image_info == (ImageInfo *) NULL)
4321 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4323 GetImageInfo(alpha_image_info);
4324 alpha_image=AcquireImage(alpha_image_info,exception);
4326 if (alpha_image == (Image *) NULL)
4328 alpha_image=DestroyImage(alpha_image);
4329 ThrowReaderException(ResourceLimitError,
4330 "MemoryAllocationFailed");
4333 if (logging != MagickFalse)
4334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4335 " Creating alpha_blob.");
4337 (void) AcquireUniqueFilename(alpha_image->filename);
4338 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4341 if (status == MagickFalse)
4342 return((Image *) NULL);
4344 if (jng_alpha_compression_method == 0)
4349 if (logging != MagickFalse)
4350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4351 " Writing IHDR chunk to alpha_blob.");
4353 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4354 "\211PNG\r\n\032\n");
4356 (void) WriteBlobMSBULong(alpha_image,13L);
4357 PNGType(data,mng_IHDR);
4358 LogPNGChunk(logging,mng_IHDR,13L);
4359 PNGLong(data+4,jng_width);
4360 PNGLong(data+8,jng_height);
4361 data[12]=jng_alpha_sample_depth;
4362 data[13]=0; /* color_type gray */
4363 data[14]=0; /* compression method 0 */
4364 data[15]=0; /* filter_method 0 */
4365 data[16]=0; /* interlace_method 0 */
4366 (void) WriteBlob(alpha_image,17,data);
4367 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4370 reading_idat=MagickTrue;
4373 if (memcmp(type,mng_JDAT,4) == 0)
4375 /* Copy chunk to color_image->blob */
4377 if (logging != MagickFalse)
4378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4379 " Copying JDAT chunk data to color_blob.");
4381 (void) WriteBlob(color_image,length,chunk);
4384 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4389 if (memcmp(type,mng_IDAT,4) == 0)
4394 /* Copy IDAT header and chunk data to alpha_image->blob */
4396 if (alpha_image != NULL && image_info->ping == MagickFalse)
4398 if (logging != MagickFalse)
4399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4400 " Copying IDAT chunk data to alpha_blob.");
4402 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4403 PNGType(data,mng_IDAT);
4404 LogPNGChunk(logging,mng_IDAT,length);
4405 (void) WriteBlob(alpha_image,4,data);
4406 (void) WriteBlob(alpha_image,length,chunk);
4407 (void) WriteBlobMSBULong(alpha_image,
4408 crc32(crc32(0,data,4),chunk,(uInt) length));
4412 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4417 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4419 /* Copy chunk data to alpha_image->blob */
4421 if (alpha_image != NULL && image_info->ping == MagickFalse)
4423 if (logging != MagickFalse)
4424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4425 " Copying JDAA chunk data to alpha_blob.");
4427 (void) WriteBlob(alpha_image,length,chunk);
4431 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4436 if (memcmp(type,mng_JSEP,4) == 0)
4438 read_JSEP=MagickTrue;
4441 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4446 if (memcmp(type,mng_bKGD,4) == 0)
4450 image->background_color.red=ScaleCharToQuantum(p[1]);
4451 image->background_color.green=image->background_color.red;
4452 image->background_color.blue=image->background_color.red;
4457 image->background_color.red=ScaleCharToQuantum(p[1]);
4458 image->background_color.green=ScaleCharToQuantum(p[3]);
4459 image->background_color.blue=ScaleCharToQuantum(p[5]);
4462 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4466 if (memcmp(type,mng_gAMA,4) == 0)
4469 image->gamma=((float) mng_get_long(p))*0.00001;
4471 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4475 if (memcmp(type,mng_cHRM,4) == 0)
4479 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4480 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4481 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4482 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4483 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4484 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4485 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4486 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4489 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4493 if (memcmp(type,mng_sRGB,4) == 0)
4497 image->rendering_intent=
4498 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4499 image->gamma=1.000f/2.200f;
4500 image->chromaticity.red_primary.x=0.6400f;
4501 image->chromaticity.red_primary.y=0.3300f;
4502 image->chromaticity.green_primary.x=0.3000f;
4503 image->chromaticity.green_primary.y=0.6000f;
4504 image->chromaticity.blue_primary.x=0.1500f;
4505 image->chromaticity.blue_primary.y=0.0600f;
4506 image->chromaticity.white_point.x=0.3127f;
4507 image->chromaticity.white_point.y=0.3290f;
4510 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4514 if (memcmp(type,mng_oFFs,4) == 0)
4518 image->page.x=(ssize_t) mng_get_long(p);
4519 image->page.y=(ssize_t) mng_get_long(&p[4]);
4521 if ((int) p[8] != 0)
4523 image->page.x/=10000;
4524 image->page.y/=10000;
4529 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4534 if (memcmp(type,mng_pHYs,4) == 0)
4538 image->resolution.x=(double) mng_get_long(p);
4539 image->resolution.y=(double) mng_get_long(&p[4]);
4540 if ((int) p[8] == PNG_RESOLUTION_METER)
4542 image->units=PixelsPerCentimeterResolution;
4543 image->resolution.x=image->resolution.x/100.0f;
4544 image->resolution.y=image->resolution.y/100.0f;
4548 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4553 if (memcmp(type,mng_iCCP,4) == 0)
4557 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4564 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4566 if (memcmp(type,mng_IEND,4))
4576 Finish up reading image data:
4578 o read main image from color_blob.
4582 o if (color_type has alpha)
4583 if alpha_encoding is PNG
4584 read secondary image from alpha_blob via ReadPNG
4585 if alpha_encoding is JPEG
4586 read secondary image from alpha_blob via ReadJPEG
4590 o copy intensity of secondary image into
4591 alpha samples of main image.
4593 o destroy the secondary image.
4596 (void) CloseBlob(color_image);
4598 if (logging != MagickFalse)
4599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4600 " Reading jng_image from color_blob.");
4602 assert(color_image_info != (ImageInfo *) NULL);
4603 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4604 color_image->filename);
4606 color_image_info->ping=MagickFalse; /* To do: avoid this */
4607 jng_image=ReadImage(color_image_info,exception);
4609 if (jng_image == (Image *) NULL)
4610 return((Image *) NULL);
4612 (void) RelinquishUniqueFileResource(color_image->filename);
4613 color_image=DestroyImage(color_image);
4614 color_image_info=DestroyImageInfo(color_image_info);
4616 if (jng_image == (Image *) NULL)
4617 return((Image *) NULL);
4619 if (logging != MagickFalse)
4620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4621 " Copying jng_image pixels to main image.");
4623 image->rows=jng_height;
4624 image->columns=jng_width;
4626 for (y=0; y < (ssize_t) image->rows; y++)
4628 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4629 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4630 for (x=(ssize_t) image->columns; x != 0; x--)
4632 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4633 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4634 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4635 q+=GetPixelChannels(image);
4636 s+=GetPixelChannels(jng_image);
4639 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4643 jng_image=DestroyImage(jng_image);
4645 if (image_info->ping == MagickFalse)
4647 if (jng_color_type >= 12)
4649 if (jng_alpha_compression_method == 0)
4653 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4654 PNGType(data,mng_IEND);
4655 LogPNGChunk(logging,mng_IEND,0L);
4656 (void) WriteBlob(alpha_image,4,data);
4657 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4660 (void) CloseBlob(alpha_image);
4662 if (logging != MagickFalse)
4663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4664 " Reading alpha from alpha_blob.");
4666 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4667 "%s",alpha_image->filename);
4669 jng_image=ReadImage(alpha_image_info,exception);
4671 if (jng_image != (Image *) NULL)
4672 for (y=0; y < (ssize_t) image->rows; y++)
4674 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4676 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4678 if (image->alpha_trait != UndefinedPixelTrait)
4679 for (x=(ssize_t) image->columns; x != 0; x--)
4681 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4682 q+=GetPixelChannels(image);
4683 s+=GetPixelChannels(jng_image);
4687 for (x=(ssize_t) image->columns; x != 0; x--)
4689 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4690 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4691 image->alpha_trait=BlendPixelTrait;
4692 q+=GetPixelChannels(image);
4693 s+=GetPixelChannels(jng_image);
4696 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4699 (void) RelinquishUniqueFileResource(alpha_image->filename);
4700 alpha_image=DestroyImage(alpha_image);
4701 alpha_image_info=DestroyImageInfo(alpha_image_info);
4702 if (jng_image != (Image *) NULL)
4703 jng_image=DestroyImage(jng_image);
4707 /* Read the JNG image. */
4709 if (mng_info->mng_type == 0)
4711 mng_info->mng_width=jng_width;
4712 mng_info->mng_height=jng_height;
4715 if (image->page.width == 0 && image->page.height == 0)
4717 image->page.width=jng_width;
4718 image->page.height=jng_height;
4721 if (image->page.x == 0 && image->page.y == 0)
4723 image->page.x=mng_info->x_off[mng_info->object_id];
4724 image->page.y=mng_info->y_off[mng_info->object_id];
4729 image->page.y=mng_info->y_off[mng_info->object_id];
4732 mng_info->image_found++;
4733 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4734 2*GetBlobSize(image));
4736 if (status == MagickFalse)
4737 return((Image *) NULL);
4739 if (logging != MagickFalse)
4740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4741 " exit ReadOneJNGImage()");
4747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4751 % R e a d J N G I m a g e %
4755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4757 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4758 % (including the 8-byte signature) and returns it. It allocates the memory
4759 % necessary for the new Image structure and returns a pointer to the new
4762 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4764 % The format of the ReadJNGImage method is:
4766 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4769 % A description of each parameter follows:
4771 % o image_info: the image info.
4773 % o exception: return any errors or warnings in this structure.
4777 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4791 magic_number[MaxTextExtent];
4799 assert(image_info != (const ImageInfo *) NULL);
4800 assert(image_info->signature == MagickSignature);
4801 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4802 assert(exception != (ExceptionInfo *) NULL);
4803 assert(exception->signature == MagickSignature);
4804 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4805 image=AcquireImage(image_info,exception);
4806 mng_info=(MngInfo *) NULL;
4807 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4809 if (status == MagickFalse)
4810 return((Image *) NULL);
4812 if (LocaleCompare(image_info->magick,"JNG") != 0)
4813 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4815 /* Verify JNG signature. */
4817 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4819 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4820 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4822 /* Allocate a MngInfo structure. */
4824 have_mng_structure=MagickFalse;
4825 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4827 if (mng_info == (MngInfo *) NULL)
4828 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4830 /* Initialize members of the MngInfo structure. */
4832 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4833 have_mng_structure=MagickTrue;
4835 mng_info->image=image;
4836 image=ReadOneJNGImage(mng_info,image_info,exception);
4837 MngInfoFreeStruct(mng_info,&have_mng_structure);
4839 if (image == (Image *) NULL)
4841 if (logging != MagickFalse)
4842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4843 "exit ReadJNGImage() with error");
4845 return((Image *) NULL);
4847 (void) CloseBlob(image);
4849 if (image->columns == 0 || image->rows == 0)
4851 if (logging != MagickFalse)
4852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4853 "exit ReadJNGImage() with error");
4855 ThrowReaderException(CorruptImageError,"CorruptImage");
4858 if (logging != MagickFalse)
4859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4865 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4868 page_geometry[MaxTextExtent];
4900 #if defined(MNG_INSERT_LAYERS)
4902 mng_background_color;
4905 register unsigned char
4920 #if defined(MNG_INSERT_LAYERS)
4925 volatile unsigned int
4926 #ifdef MNG_OBJECT_BUFFERS
4927 mng_background_object=0,
4929 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4932 default_frame_timeout,
4934 #if defined(MNG_INSERT_LAYERS)
4940 /* These delays are all measured in image ticks_per_second,
4941 * not in MNG ticks_per_second
4944 default_frame_delay,
4948 #if defined(MNG_INSERT_LAYERS)
4957 previous_fb.bottom=0;
4959 previous_fb.right=0;
4961 default_fb.bottom=0;
4965 /* Open image file. */
4967 assert(image_info != (const ImageInfo *) NULL);
4968 assert(image_info->signature == MagickSignature);
4969 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4970 assert(exception != (ExceptionInfo *) NULL);
4971 assert(exception->signature == MagickSignature);
4972 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4973 image=AcquireImage(image_info,exception);
4974 mng_info=(MngInfo *) NULL;
4975 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4977 if (status == MagickFalse)
4978 return((Image *) NULL);
4980 first_mng_object=MagickFalse;
4982 have_mng_structure=MagickFalse;
4984 /* Allocate a MngInfo structure. */
4986 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4988 if (mng_info == (MngInfo *) NULL)
4989 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4991 /* Initialize members of the MngInfo structure. */
4993 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4994 mng_info->image=image;
4995 have_mng_structure=MagickTrue;
4997 if (LocaleCompare(image_info->magick,"MNG") == 0)
5000 magic_number[MaxTextExtent];
5002 /* Verify MNG signature. */
5003 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5004 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5005 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5007 /* Initialize some nonzero members of the MngInfo structure. */
5008 for (i=0; i < MNG_MAX_OBJECTS; i++)
5010 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5011 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5013 mng_info->exists[0]=MagickTrue;
5016 first_mng_object=MagickTrue;
5018 #if defined(MNG_INSERT_LAYERS)
5019 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5021 default_frame_delay=0;
5022 default_frame_timeout=0;
5025 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5027 skip_to_iend=MagickFalse;
5028 term_chunk_found=MagickFalse;
5029 mng_info->framing_mode=1;
5030 #if defined(MNG_INSERT_LAYERS)
5031 mandatory_back=MagickFalse;
5033 #if defined(MNG_INSERT_LAYERS)
5034 mng_background_color=image->background_color;
5036 default_fb=mng_info->frame;
5037 previous_fb=mng_info->frame;
5041 type[MaxTextExtent];
5043 if (LocaleCompare(image_info->magick,"MNG") == 0)
5052 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5053 length=ReadBlobMSBLong(image);
5054 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5056 if (logging != MagickFalse)
5057 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5058 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5059 type[0],type[1],type[2],type[3],(double) length);
5061 if (length > PNG_UINT_31_MAX)
5068 ThrowReaderException(CorruptImageError,"CorruptImage");
5071 chunk=(unsigned char *) NULL;
5075 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5077 if (chunk == (unsigned char *) NULL)
5078 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5080 for (i=0; i < (ssize_t) length; i++)
5081 chunk[i]=(unsigned char) ReadBlobByte(image);
5086 (void) ReadBlobMSBLong(image); /* read crc word */
5088 #if !defined(JNG_SUPPORTED)
5089 if (memcmp(type,mng_JHDR,4) == 0)
5091 skip_to_iend=MagickTrue;
5093 if (mng_info->jhdr_warning == 0)
5094 (void) ThrowMagickException(exception,GetMagickModule(),
5095 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5097 mng_info->jhdr_warning++;
5100 if (memcmp(type,mng_DHDR,4) == 0)
5102 skip_to_iend=MagickTrue;
5104 if (mng_info->dhdr_warning == 0)
5105 (void) ThrowMagickException(exception,GetMagickModule(),
5106 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5108 mng_info->dhdr_warning++;
5110 if (memcmp(type,mng_MEND,4) == 0)
5115 if (memcmp(type,mng_IEND,4) == 0)
5116 skip_to_iend=MagickFalse;
5119 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5121 if (logging != MagickFalse)
5122 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5128 if (memcmp(type,mng_MHDR,4) == 0)
5130 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5131 (p[2] << 8) | p[3]);
5133 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5134 (p[6] << 8) | p[7]);
5136 if (logging != MagickFalse)
5138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5139 " MNG width: %.20g",(double) mng_info->mng_width);
5140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5141 " MNG height: %.20g",(double) mng_info->mng_height);
5145 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5147 if (mng_info->ticks_per_second == 0)
5148 default_frame_delay=0;
5151 default_frame_delay=1UL*image->ticks_per_second/
5152 mng_info->ticks_per_second;
5154 frame_delay=default_frame_delay;
5160 simplicity=(size_t) mng_get_long(p);
5163 mng_type=1; /* Full MNG */
5165 if ((simplicity != 0) && ((simplicity | 11) == 11))
5166 mng_type=2; /* LC */
5168 if ((simplicity != 0) && ((simplicity | 9) == 9))
5169 mng_type=3; /* VLC */
5171 #if defined(MNG_INSERT_LAYERS)
5173 insert_layers=MagickTrue;
5175 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5177 /* Allocate next image structure. */
5178 AcquireNextImage(image_info,image,exception);
5180 if (GetNextImageInList(image) == (Image *) NULL)
5181 return((Image *) NULL);
5183 image=SyncNextImageInList(image);
5184 mng_info->image=image;
5187 if ((mng_info->mng_width > 65535L) ||
5188 (mng_info->mng_height > 65535L))
5189 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5191 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5192 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5193 mng_info->mng_height);
5195 mng_info->frame.left=0;
5196 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5197 mng_info->frame.top=0;
5198 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5199 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5201 for (i=0; i < MNG_MAX_OBJECTS; i++)
5202 mng_info->object_clip[i]=mng_info->frame;
5204 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5208 if (memcmp(type,mng_TERM,4) == 0)
5219 final_delay=(png_uint_32) mng_get_long(&p[2]);
5220 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5222 if (mng_iterations == PNG_UINT_31_MAX)
5225 image->iterations=mng_iterations;
5226 term_chunk_found=MagickTrue;
5229 if (logging != MagickFalse)
5231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5232 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5233 repeat,(double) final_delay, (double) image->iterations);
5236 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5239 if (memcmp(type,mng_DEFI,4) == 0)
5242 (void) ThrowMagickException(exception,GetMagickModule(),
5243 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5246 object_id=(p[0] << 8) | p[1];
5248 if (mng_type == 2 && object_id != 0)
5249 (void) ThrowMagickException(exception,GetMagickModule(),
5250 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5253 if (object_id > MNG_MAX_OBJECTS)
5256 Instead of using a warning we should allocate a larger
5257 MngInfo structure and continue.
5259 (void) ThrowMagickException(exception,GetMagickModule(),
5260 CoderError,"object id too large","`%s'",image->filename);
5261 object_id=MNG_MAX_OBJECTS;
5264 if (mng_info->exists[object_id])
5265 if (mng_info->frozen[object_id])
5267 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5268 (void) ThrowMagickException(exception,
5269 GetMagickModule(),CoderError,
5270 "DEFI cannot redefine a frozen MNG object","`%s'",
5275 mng_info->exists[object_id]=MagickTrue;
5278 mng_info->invisible[object_id]=p[2];
5281 Extract object offset info.
5285 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5286 (p[5] << 16) | (p[6] << 8) | p[7]);
5288 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5289 (p[9] << 16) | (p[10] << 8) | p[11]);
5291 if (logging != MagickFalse)
5293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5294 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5295 object_id,(double) mng_info->x_off[object_id],
5296 object_id,(double) mng_info->y_off[object_id]);
5301 Extract object clipping info.
5304 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5307 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5310 if (memcmp(type,mng_bKGD,4) == 0)
5312 mng_info->have_global_bkgd=MagickFalse;
5316 mng_info->mng_global_bkgd.red=
5317 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5319 mng_info->mng_global_bkgd.green=
5320 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5322 mng_info->mng_global_bkgd.blue=
5323 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5325 mng_info->have_global_bkgd=MagickTrue;
5328 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5331 if (memcmp(type,mng_BACK,4) == 0)
5333 #if defined(MNG_INSERT_LAYERS)
5335 mandatory_back=p[6];
5340 if (mandatory_back && length > 5)
5342 mng_background_color.red=
5343 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5345 mng_background_color.green=
5346 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5348 mng_background_color.blue=
5349 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5351 mng_background_color.alpha=OpaqueAlpha;
5354 #ifdef MNG_OBJECT_BUFFERS
5356 mng_background_object=(p[7] << 8) | p[8];
5359 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5363 if (memcmp(type,mng_PLTE,4) == 0)
5365 /* Read global PLTE. */
5367 if (length && (length < 769))
5369 if (mng_info->global_plte == (png_colorp) NULL)
5370 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5371 sizeof(*mng_info->global_plte));
5373 for (i=0; i < (ssize_t) (length/3); i++)
5375 mng_info->global_plte[i].red=p[3*i];
5376 mng_info->global_plte[i].green=p[3*i+1];
5377 mng_info->global_plte[i].blue=p[3*i+2];
5380 mng_info->global_plte_length=(unsigned int) (length/3);
5383 for ( ; i < 256; i++)
5385 mng_info->global_plte[i].red=i;
5386 mng_info->global_plte[i].green=i;
5387 mng_info->global_plte[i].blue=i;
5391 mng_info->global_plte_length=256;
5394 mng_info->global_plte_length=0;
5396 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5400 if (memcmp(type,mng_tRNS,4) == 0)
5402 /* read global tRNS */
5405 for (i=0; i < (ssize_t) length; i++)
5406 mng_info->global_trns[i]=p[i];
5409 for ( ; i < 256; i++)
5410 mng_info->global_trns[i]=255;
5412 mng_info->global_trns_length=(unsigned int) length;
5413 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5416 if (memcmp(type,mng_gAMA,4) == 0)
5423 igamma=mng_get_long(p);
5424 mng_info->global_gamma=((float) igamma)*0.00001;
5425 mng_info->have_global_gama=MagickTrue;
5429 mng_info->have_global_gama=MagickFalse;
5431 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5435 if (memcmp(type,mng_cHRM,4) == 0)
5437 /* Read global cHRM */
5441 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5442 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5443 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5444 mng_info->global_chrm.red_primary.y=0.00001*
5445 mng_get_long(&p[12]);
5446 mng_info->global_chrm.green_primary.x=0.00001*
5447 mng_get_long(&p[16]);
5448 mng_info->global_chrm.green_primary.y=0.00001*
5449 mng_get_long(&p[20]);
5450 mng_info->global_chrm.blue_primary.x=0.00001*
5451 mng_get_long(&p[24]);
5452 mng_info->global_chrm.blue_primary.y=0.00001*
5453 mng_get_long(&p[28]);
5454 mng_info->have_global_chrm=MagickTrue;
5457 mng_info->have_global_chrm=MagickFalse;
5459 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5463 if (memcmp(type,mng_sRGB,4) == 0)
5470 mng_info->global_srgb_intent=
5471 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5472 mng_info->have_global_srgb=MagickTrue;
5475 mng_info->have_global_srgb=MagickFalse;
5477 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5481 if (memcmp(type,mng_iCCP,4) == 0)
5489 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5494 if (memcmp(type,mng_FRAM,4) == 0)
5497 (void) ThrowMagickException(exception,GetMagickModule(),
5498 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5501 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5502 image->delay=frame_delay;
5504 frame_delay=default_frame_delay;
5505 frame_timeout=default_frame_timeout;
5510 mng_info->framing_mode=p[0];
5512 if (logging != MagickFalse)
5513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5514 " Framing_mode=%d",mng_info->framing_mode);
5518 /* Note the delay and frame clipping boundaries. */
5520 p++; /* framing mode */
5522 while (*p && ((p-chunk) < (ssize_t) length))
5523 p++; /* frame name */
5525 p++; /* frame name terminator */
5527 if ((p-chunk) < (ssize_t) (length-4))
5534 change_delay=(*p++);
5535 change_timeout=(*p++);
5536 change_clipping=(*p++);
5537 p++; /* change_sync */
5541 frame_delay=1UL*image->ticks_per_second*
5544 if (mng_info->ticks_per_second != 0)
5545 frame_delay/=mng_info->ticks_per_second;
5548 frame_delay=PNG_UINT_31_MAX;
5550 if (change_delay == 2)
5551 default_frame_delay=frame_delay;
5555 if (logging != MagickFalse)
5556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5557 " Framing_delay=%.20g",(double) frame_delay);
5562 frame_timeout=1UL*image->ticks_per_second*
5565 if (mng_info->ticks_per_second != 0)
5566 frame_timeout/=mng_info->ticks_per_second;
5569 frame_timeout=PNG_UINT_31_MAX;
5571 if (change_timeout == 2)
5572 default_frame_timeout=frame_timeout;
5576 if (logging != MagickFalse)
5577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5578 " Framing_timeout=%.20g",(double) frame_timeout);
5581 if (change_clipping)
5583 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5587 if (logging != MagickFalse)
5588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5589 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5590 (double) fb.left,(double) fb.right,(double) fb.top,
5591 (double) fb.bottom);
5593 if (change_clipping == 2)
5599 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5601 subframe_width=(size_t) (mng_info->clip.right
5602 -mng_info->clip.left);
5604 subframe_height=(size_t) (mng_info->clip.bottom
5605 -mng_info->clip.top);
5607 Insert a background layer behind the frame if framing_mode is 4.
5609 #if defined(MNG_INSERT_LAYERS)
5610 if (logging != MagickFalse)
5611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5612 " subframe_width=%.20g, subframe_height=%.20g",(double)
5613 subframe_width,(double) subframe_height);
5615 if (insert_layers && (mng_info->framing_mode == 4) &&
5616 (subframe_width) && (subframe_height))
5618 /* Allocate next image structure. */
5619 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5621 AcquireNextImage(image_info,image,exception);
5623 if (GetNextImageInList(image) == (Image *) NULL)
5625 image=DestroyImageList(image);
5626 MngInfoFreeStruct(mng_info,&have_mng_structure);
5627 return((Image *) NULL);
5630 image=SyncNextImageInList(image);
5633 mng_info->image=image;
5635 if (term_chunk_found)
5637 image->start_loop=MagickTrue;
5638 image->iterations=mng_iterations;
5639 term_chunk_found=MagickFalse;
5643 image->start_loop=MagickFalse;
5645 image->columns=subframe_width;
5646 image->rows=subframe_height;
5647 image->page.width=subframe_width;
5648 image->page.height=subframe_height;
5649 image->page.x=mng_info->clip.left;
5650 image->page.y=mng_info->clip.top;
5651 image->background_color=mng_background_color;
5652 image->alpha_trait=UndefinedPixelTrait;
5654 (void) SetImageBackgroundColor(image,exception);
5656 if (logging != MagickFalse)
5657 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5658 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5659 (double) mng_info->clip.left,(double) mng_info->clip.right,
5660 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5663 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5667 if (memcmp(type,mng_CLIP,4) == 0)
5678 first_object=(p[0] << 8) | p[1];
5679 last_object=(p[2] << 8) | p[3];
5682 for (i=(int) first_object; i <= (int) last_object; i++)
5684 if (mng_info->exists[i] && !mng_info->frozen[i])
5689 box=mng_info->object_clip[i];
5690 if ((p-chunk) < (ssize_t) (length-17))
5691 mng_info->object_clip[i]=
5692 mng_read_box(box,(char) p[0],&p[1]);
5697 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5701 if (memcmp(type,mng_SAVE,4) == 0)
5703 for (i=1; i < MNG_MAX_OBJECTS; i++)
5704 if (mng_info->exists[i])
5706 mng_info->frozen[i]=MagickTrue;
5707 #ifdef MNG_OBJECT_BUFFERS
5708 if (mng_info->ob[i] != (MngBuffer *) NULL)
5709 mng_info->ob[i]->frozen=MagickTrue;
5714 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5719 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5721 /* Read DISC or SEEK. */
5723 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5725 for (i=1; i < MNG_MAX_OBJECTS; i++)
5726 MngInfoDiscardObject(mng_info,i);
5734 for (j=0; j < (ssize_t) length; j+=2)
5736 i=p[j] << 8 | p[j+1];
5737 MngInfoDiscardObject(mng_info,i);
5742 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5747 if (memcmp(type,mng_MOVE,4) == 0)
5757 first_object=(p[0] << 8) | p[1];
5758 last_object=(p[2] << 8) | p[3];
5761 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5763 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5764 (p-chunk) < (ssize_t) (length-8))
5772 old_pair.a=mng_info->x_off[i];
5773 old_pair.b=mng_info->y_off[i];
5774 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
5775 mng_info->x_off[i]=new_pair.a;
5776 mng_info->y_off[i]=new_pair.b;
5781 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5785 if (memcmp(type,mng_LOOP,4) == 0)
5787 ssize_t loop_iters=1;
5790 loop_level=chunk[0];
5791 mng_info->loop_active[loop_level]=1; /* mark loop active */
5793 /* Record starting point. */
5794 loop_iters=mng_get_long(&chunk[1]);
5796 if (logging != MagickFalse)
5797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5798 " LOOP level %.20g has %.20g iterations ",
5799 (double) loop_level, (double) loop_iters);
5801 if (loop_iters == 0)
5802 skipping_loop=loop_level;
5806 mng_info->loop_jump[loop_level]=TellBlob(image);
5807 mng_info->loop_count[loop_level]=loop_iters;
5810 mng_info->loop_iteration[loop_level]=0;
5812 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5816 if (memcmp(type,mng_ENDL,4) == 0)
5820 loop_level=chunk[0];
5822 if (skipping_loop > 0)
5824 if (skipping_loop == loop_level)
5827 Found end of zero-iteration loop.
5830 mng_info->loop_active[loop_level]=0;
5836 if (mng_info->loop_active[loop_level] == 1)
5838 mng_info->loop_count[loop_level]--;
5839 mng_info->loop_iteration[loop_level]++;
5841 if (logging != MagickFalse)
5842 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5843 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5844 (double) loop_level,(double)
5845 mng_info->loop_count[loop_level]);
5847 if (mng_info->loop_count[loop_level] != 0)
5850 SeekBlob(image,mng_info->loop_jump[loop_level],
5854 ThrowReaderException(CorruptImageError,
5855 "ImproperImageHeader");
5866 mng_info->loop_active[loop_level]=0;
5868 for (i=0; i < loop_level; i++)
5869 if (mng_info->loop_active[i] == 1)
5870 last_level=(short) i;
5871 loop_level=last_level;
5877 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5881 if (memcmp(type,mng_CLON,4) == 0)
5883 if (mng_info->clon_warning == 0)
5884 (void) ThrowMagickException(exception,GetMagickModule(),
5885 CoderError,"CLON is not implemented yet","`%s'",
5888 mng_info->clon_warning++;
5891 if (memcmp(type,mng_MAGN,4) == 0)
5906 magn_first=(p[0] << 8) | p[1];
5912 magn_last=(p[2] << 8) | p[3];
5915 magn_last=magn_first;
5916 #ifndef MNG_OBJECT_BUFFERS
5917 if (magn_first || magn_last)
5918 if (mng_info->magn_warning == 0)
5920 (void) ThrowMagickException(exception,
5921 GetMagickModule(),CoderError,
5922 "MAGN is not implemented yet for nonzero objects",
5923 "`%s'",image->filename);
5925 mng_info->magn_warning++;
5935 magn_mx=(p[5] << 8) | p[6];
5944 magn_my=(p[7] << 8) | p[8];
5953 magn_ml=(p[9] << 8) | p[10];
5962 magn_mr=(p[11] << 8) | p[12];
5971 magn_mt=(p[13] << 8) | p[14];
5980 magn_mb=(p[15] << 8) | p[16];
5992 magn_methy=magn_methx;
5995 if (magn_methx > 5 || magn_methy > 5)
5996 if (mng_info->magn_warning == 0)
5998 (void) ThrowMagickException(exception,
5999 GetMagickModule(),CoderError,
6000 "Unknown MAGN method in MNG datastream","`%s'",
6003 mng_info->magn_warning++;
6005 #ifdef MNG_OBJECT_BUFFERS
6006 /* Magnify existing objects in the range magn_first to magn_last */
6008 if (magn_first == 0 || magn_last == 0)
6010 /* Save the magnification factors for object 0 */
6011 mng_info->magn_mb=magn_mb;
6012 mng_info->magn_ml=magn_ml;
6013 mng_info->magn_mr=magn_mr;
6014 mng_info->magn_mt=magn_mt;
6015 mng_info->magn_mx=magn_mx;
6016 mng_info->magn_my=magn_my;
6017 mng_info->magn_methx=magn_methx;
6018 mng_info->magn_methy=magn_methy;
6022 if (memcmp(type,mng_PAST,4) == 0)
6024 if (mng_info->past_warning == 0)
6025 (void) ThrowMagickException(exception,GetMagickModule(),
6026 CoderError,"PAST is not implemented yet","`%s'",
6029 mng_info->past_warning++;
6032 if (memcmp(type,mng_SHOW,4) == 0)
6034 if (mng_info->show_warning == 0)
6035 (void) ThrowMagickException(exception,GetMagickModule(),
6036 CoderError,"SHOW is not implemented yet","`%s'",
6039 mng_info->show_warning++;
6042 if (memcmp(type,mng_sBIT,4) == 0)
6045 mng_info->have_global_sbit=MagickFalse;
6049 mng_info->global_sbit.gray=p[0];
6050 mng_info->global_sbit.red=p[0];
6051 mng_info->global_sbit.green=p[1];
6052 mng_info->global_sbit.blue=p[2];
6053 mng_info->global_sbit.alpha=p[3];
6054 mng_info->have_global_sbit=MagickTrue;
6057 if (memcmp(type,mng_pHYs,4) == 0)
6061 mng_info->global_x_pixels_per_unit=
6062 (size_t) mng_get_long(p);
6063 mng_info->global_y_pixels_per_unit=
6064 (size_t) mng_get_long(&p[4]);
6065 mng_info->global_phys_unit_type=p[8];
6066 mng_info->have_global_phys=MagickTrue;
6070 mng_info->have_global_phys=MagickFalse;
6072 if (memcmp(type,mng_pHYg,4) == 0)
6074 if (mng_info->phyg_warning == 0)
6075 (void) ThrowMagickException(exception,GetMagickModule(),
6076 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6078 mng_info->phyg_warning++;
6080 if (memcmp(type,mng_BASI,4) == 0)
6082 skip_to_iend=MagickTrue;
6084 if (mng_info->basi_warning == 0)
6085 (void) ThrowMagickException(exception,GetMagickModule(),
6086 CoderError,"BASI is not implemented yet","`%s'",
6089 mng_info->basi_warning++;
6090 #ifdef MNG_BASI_SUPPORTED
6091 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6092 (p[2] << 8) | p[3]);
6093 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6094 (p[6] << 8) | p[7]);
6095 basi_color_type=p[8];
6096 basi_compression_method=p[9];
6097 basi_filter_type=p[10];
6098 basi_interlace_method=p[11];
6100 basi_red=(p[12] << 8) & p[13];
6106 basi_green=(p[14] << 8) & p[15];
6112 basi_blue=(p[16] << 8) & p[17];
6118 basi_alpha=(p[18] << 8) & p[19];
6122 if (basi_sample_depth == 16)
6129 basi_viewable=p[20];
6135 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6139 if (memcmp(type,mng_IHDR,4)
6140 #if defined(JNG_SUPPORTED)
6141 && memcmp(type,mng_JHDR,4)
6145 /* Not an IHDR or JHDR chunk */
6147 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6152 if (logging != MagickFalse)
6153 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6154 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6156 mng_info->exists[object_id]=MagickTrue;
6157 mng_info->viewable[object_id]=MagickTrue;
6159 if (mng_info->invisible[object_id])
6161 if (logging != MagickFalse)
6162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6163 " Skipping invisible object");
6165 skip_to_iend=MagickTrue;
6166 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6169 #if defined(MNG_INSERT_LAYERS)
6171 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6173 image_width=(size_t) mng_get_long(p);
6174 image_height=(size_t) mng_get_long(&p[4]);
6176 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6179 Insert a transparent background layer behind the entire animation
6180 if it is not full screen.
6182 #if defined(MNG_INSERT_LAYERS)
6183 if (insert_layers && mng_type && first_mng_object)
6185 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6186 (image_width < mng_info->mng_width) ||
6187 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6188 (image_height < mng_info->mng_height) ||
6189 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6191 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6194 Allocate next image structure.
6196 AcquireNextImage(image_info,image,exception);
6198 if (GetNextImageInList(image) == (Image *) NULL)
6200 image=DestroyImageList(image);
6201 MngInfoFreeStruct(mng_info,&have_mng_structure);
6202 return((Image *) NULL);
6205 image=SyncNextImageInList(image);
6207 mng_info->image=image;
6209 if (term_chunk_found)
6211 image->start_loop=MagickTrue;
6212 image->iterations=mng_iterations;
6213 term_chunk_found=MagickFalse;
6217 image->start_loop=MagickFalse;
6219 /* Make a background rectangle. */
6222 image->columns=mng_info->mng_width;
6223 image->rows=mng_info->mng_height;
6224 image->page.width=mng_info->mng_width;
6225 image->page.height=mng_info->mng_height;
6228 image->background_color=mng_background_color;
6229 (void) SetImageBackgroundColor(image,exception);
6230 if (logging != MagickFalse)
6231 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6232 " Inserted transparent background layer, W=%.20g, H=%.20g",
6233 (double) mng_info->mng_width,(double) mng_info->mng_height);
6237 Insert a background layer behind the upcoming image if
6238 framing_mode is 3, and we haven't already inserted one.
6240 if (insert_layers && (mng_info->framing_mode == 3) &&
6241 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6242 (simplicity & 0x08)))
6244 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6247 Allocate next image structure.
6249 AcquireNextImage(image_info,image,exception);
6251 if (GetNextImageInList(image) == (Image *) NULL)
6253 image=DestroyImageList(image);
6254 MngInfoFreeStruct(mng_info,&have_mng_structure);
6255 return((Image *) NULL);
6258 image=SyncNextImageInList(image);
6261 mng_info->image=image;
6263 if (term_chunk_found)
6265 image->start_loop=MagickTrue;
6266 image->iterations=mng_iterations;
6267 term_chunk_found=MagickFalse;
6271 image->start_loop=MagickFalse;
6274 image->columns=subframe_width;
6275 image->rows=subframe_height;
6276 image->page.width=subframe_width;
6277 image->page.height=subframe_height;
6278 image->page.x=mng_info->clip.left;
6279 image->page.y=mng_info->clip.top;
6280 image->background_color=mng_background_color;
6281 image->alpha_trait=UndefinedPixelTrait;
6282 (void) SetImageBackgroundColor(image,exception);
6284 if (logging != MagickFalse)
6285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6286 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6287 (double) mng_info->clip.left,(double) mng_info->clip.right,
6288 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6290 #endif /* MNG_INSERT_LAYERS */
6291 first_mng_object=MagickFalse;
6293 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6296 Allocate next image structure.
6298 AcquireNextImage(image_info,image,exception);
6300 if (GetNextImageInList(image) == (Image *) NULL)
6302 image=DestroyImageList(image);
6303 MngInfoFreeStruct(mng_info,&have_mng_structure);
6304 return((Image *) NULL);
6307 image=SyncNextImageInList(image);
6309 mng_info->image=image;
6310 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6311 GetBlobSize(image));
6313 if (status == MagickFalse)
6316 if (term_chunk_found)
6318 image->start_loop=MagickTrue;
6319 term_chunk_found=MagickFalse;
6323 image->start_loop=MagickFalse;
6325 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6327 image->delay=frame_delay;
6328 frame_delay=default_frame_delay;
6334 image->page.width=mng_info->mng_width;
6335 image->page.height=mng_info->mng_height;
6336 image->page.x=mng_info->x_off[object_id];
6337 image->page.y=mng_info->y_off[object_id];
6338 image->iterations=mng_iterations;
6341 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6344 if (logging != MagickFalse)
6345 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6346 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6349 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6352 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6355 mng_info->image=image;
6356 mng_info->mng_type=mng_type;
6357 mng_info->object_id=object_id;
6359 if (memcmp(type,mng_IHDR,4) == 0)
6360 image=ReadOnePNGImage(mng_info,image_info,exception);
6362 #if defined(JNG_SUPPORTED)
6364 image=ReadOneJNGImage(mng_info,image_info,exception);
6367 if (image == (Image *) NULL)
6369 if (logging != MagickFalse)
6370 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6371 "exit ReadJNGImage() with error");
6373 MngInfoFreeStruct(mng_info,&have_mng_structure);
6374 return((Image *) NULL);
6377 if (image->columns == 0 || image->rows == 0)
6379 (void) CloseBlob(image);
6380 image=DestroyImageList(image);
6381 MngInfoFreeStruct(mng_info,&have_mng_structure);
6382 return((Image *) NULL);
6385 mng_info->image=image;
6392 if (mng_info->magn_methx || mng_info->magn_methy)
6398 if (logging != MagickFalse)
6399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6400 " Processing MNG MAGN chunk");
6402 if (mng_info->magn_methx == 1)
6404 magnified_width=mng_info->magn_ml;
6406 if (image->columns > 1)
6407 magnified_width += mng_info->magn_mr;
6409 if (image->columns > 2)
6410 magnified_width += (png_uint_32)
6411 ((image->columns-2)*(mng_info->magn_mx));
6416 magnified_width=(png_uint_32) image->columns;
6418 if (image->columns > 1)
6419 magnified_width += mng_info->magn_ml-1;
6421 if (image->columns > 2)
6422 magnified_width += mng_info->magn_mr-1;
6424 if (image->columns > 3)
6425 magnified_width += (png_uint_32)
6426 ((image->columns-3)*(mng_info->magn_mx-1));
6429 if (mng_info->magn_methy == 1)
6431 magnified_height=mng_info->magn_mt;
6433 if (image->rows > 1)
6434 magnified_height += mng_info->magn_mb;
6436 if (image->rows > 2)
6437 magnified_height += (png_uint_32)
6438 ((image->rows-2)*(mng_info->magn_my));
6443 magnified_height=(png_uint_32) image->rows;
6445 if (image->rows > 1)
6446 magnified_height += mng_info->magn_mt-1;
6448 if (image->rows > 2)
6449 magnified_height += mng_info->magn_mb-1;
6451 if (image->rows > 3)
6452 magnified_height += (png_uint_32)
6453 ((image->rows-3)*(mng_info->magn_my-1));
6456 if (magnified_height > image->rows ||
6457 magnified_width > image->columns)
6484 /* Allocate next image structure. */
6486 if (logging != MagickFalse)
6487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6488 " Allocate magnified image");
6490 AcquireNextImage(image_info,image,exception);
6492 if (GetNextImageInList(image) == (Image *) NULL)
6494 image=DestroyImageList(image);
6495 MngInfoFreeStruct(mng_info,&have_mng_structure);
6496 return((Image *) NULL);
6499 large_image=SyncNextImageInList(image);
6501 large_image->columns=magnified_width;
6502 large_image->rows=magnified_height;
6504 magn_methx=mng_info->magn_methx;
6505 magn_methy=mng_info->magn_methy;
6507 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6508 #define QM unsigned short
6509 if (magn_methx != 1 || magn_methy != 1)
6512 Scale pixels to unsigned shorts to prevent
6513 overflow of intermediate values of interpolations
6515 for (y=0; y < (ssize_t) image->rows; y++)
6517 q=GetAuthenticPixels(image,0,y,image->columns,1,
6520 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6522 SetPixelRed(image,ScaleQuantumToShort(
6523 GetPixelRed(image,q)),q);
6524 SetPixelGreen(image,ScaleQuantumToShort(
6525 GetPixelGreen(image,q)),q);
6526 SetPixelBlue(image,ScaleQuantumToShort(
6527 GetPixelBlue(image,q)),q);
6528 SetPixelAlpha(image,ScaleQuantumToShort(
6529 GetPixelAlpha(image,q)),q);
6530 q+=GetPixelChannels(image);
6533 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6541 if (image->alpha_trait != UndefinedPixelTrait)
6542 (void) SetImageBackgroundColor(large_image,exception);
6546 large_image->background_color.alpha=OpaqueAlpha;
6547 (void) SetImageBackgroundColor(large_image,exception);
6549 if (magn_methx == 4)
6552 if (magn_methx == 5)
6555 if (magn_methy == 4)
6558 if (magn_methy == 5)
6562 /* magnify the rows into the right side of the large image */
6564 if (logging != MagickFalse)
6565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6566 " Magnify the rows to %.20g",(double) large_image->rows);
6567 m=(ssize_t) mng_info->magn_mt;
6569 length=(size_t) GetPixelChannels(image)*image->columns;
6570 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6571 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6573 if ((prev == (Quantum *) NULL) ||
6574 (next == (Quantum *) NULL))
6576 image=DestroyImageList(image);
6577 MngInfoFreeStruct(mng_info,&have_mng_structure);
6578 ThrowReaderException(ResourceLimitError,
6579 "MemoryAllocationFailed");
6582 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6583 (void) CopyMagickMemory(next,n,length);
6585 for (y=0; y < (ssize_t) image->rows; y++)
6588 m=(ssize_t) mng_info->magn_mt;
6590 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6591 m=(ssize_t) mng_info->magn_mb;
6593 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6594 m=(ssize_t) mng_info->magn_mb;
6596 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6600 m=(ssize_t) mng_info->magn_my;
6606 if (y < (ssize_t) image->rows-1)
6608 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6610 (void) CopyMagickMemory(next,n,length);
6613 for (i=0; i < m; i++, yy++)
6618 assert(yy < (ssize_t) large_image->rows);
6621 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6623 q+=(large_image->columns-image->columns)*
6624 GetPixelChannels(large_image);
6626 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6628 /* To do: get color as function of indexes[x] */
6630 if (image->storage_class == PseudoClass)
6635 if (magn_methy <= 1)
6637 /* replicate previous */
6638 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6639 SetPixelGreen(large_image,GetPixelGreen(image,
6641 SetPixelBlue(large_image,GetPixelBlue(image,
6643 SetPixelAlpha(large_image,GetPixelAlpha(image,
6647 else if (magn_methy == 2 || magn_methy == 4)
6651 SetPixelRed(large_image,GetPixelRed(image,
6653 SetPixelGreen(large_image,GetPixelGreen(image,
6655 SetPixelBlue(large_image,GetPixelBlue(image,
6657 SetPixelAlpha(large_image,GetPixelAlpha(image,
6664 SetPixelRed(large_image,((QM) (((ssize_t)
6665 (2*i*(GetPixelRed(image,n)
6666 -GetPixelRed(image,pixels)+m))/
6668 +GetPixelRed(image,pixels)))),q);
6669 SetPixelGreen(large_image,((QM) (((ssize_t)
6670 (2*i*(GetPixelGreen(image,n)
6671 -GetPixelGreen(image,pixels)+m))/
6673 +GetPixelGreen(image,pixels)))),q);
6674 SetPixelBlue(large_image,((QM) (((ssize_t)
6675 (2*i*(GetPixelBlue(image,n)
6676 -GetPixelBlue(image,pixels)+m))/
6678 +GetPixelBlue(image,pixels)))),q);
6680 if (image->alpha_trait != UndefinedPixelTrait)
6681 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6682 (2*i*(GetPixelAlpha(image,n)
6683 -GetPixelAlpha(image,pixels)+m))
6685 GetPixelAlpha(image,pixels)))),q);
6688 if (magn_methy == 4)
6690 /* Replicate nearest */
6691 if (i <= ((m+1) << 1))
6692 SetPixelAlpha(large_image,GetPixelAlpha(image,
6695 SetPixelAlpha(large_image,GetPixelAlpha(image,
6700 else /* if (magn_methy == 3 || magn_methy == 5) */
6702 /* Replicate nearest */
6703 if (i <= ((m+1) << 1))
6705 SetPixelRed(large_image,GetPixelRed(image,
6707 SetPixelGreen(large_image,GetPixelGreen(image,
6709 SetPixelBlue(large_image,GetPixelBlue(image,
6711 SetPixelAlpha(large_image,GetPixelAlpha(image,
6717 SetPixelRed(large_image,GetPixelRed(image,n),q);
6718 SetPixelGreen(large_image,GetPixelGreen(image,n),
6720 SetPixelBlue(large_image,GetPixelBlue(image,n),
6722 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6726 if (magn_methy == 5)
6728 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6729 (GetPixelAlpha(image,n)
6730 -GetPixelAlpha(image,pixels))
6731 +m))/((ssize_t) (m*2))
6732 +GetPixelAlpha(image,pixels)),q);
6735 n+=GetPixelChannels(image);
6736 q+=GetPixelChannels(large_image);
6737 pixels+=GetPixelChannels(image);
6740 if (SyncAuthenticPixels(large_image,exception) == 0)
6746 prev=(Quantum *) RelinquishMagickMemory(prev);
6747 next=(Quantum *) RelinquishMagickMemory(next);
6749 length=image->columns;
6751 if (logging != MagickFalse)
6752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6753 " Delete original image");
6755 DeleteImageFromList(&image);
6759 mng_info->image=image;
6761 /* magnify the columns */
6762 if (logging != MagickFalse)
6763 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6764 " Magnify the columns to %.20g",(double) image->columns);
6766 for (y=0; y < (ssize_t) image->rows; y++)
6771 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6772 pixels=q+(image->columns-length)*GetPixelChannels(image);
6773 n=pixels+GetPixelChannels(image);
6775 for (x=(ssize_t) (image->columns-length);
6776 x < (ssize_t) image->columns; x++)
6778 /* To do: Rewrite using Get/Set***PixelChannel() */
6780 if (x == (ssize_t) (image->columns-length))
6781 m=(ssize_t) mng_info->magn_ml;
6783 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6784 m=(ssize_t) mng_info->magn_mr;
6786 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6787 m=(ssize_t) mng_info->magn_mr;
6789 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6793 m=(ssize_t) mng_info->magn_mx;
6795 for (i=0; i < m; i++)
6797 if (magn_methx <= 1)
6799 /* replicate previous */
6800 SetPixelRed(image,GetPixelRed(image,pixels),q);
6801 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6802 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6803 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6806 else if (magn_methx == 2 || magn_methx == 4)
6810 SetPixelRed(image,GetPixelRed(image,pixels),q);
6811 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6812 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6813 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6816 /* To do: Rewrite using Get/Set***PixelChannel() */
6820 SetPixelRed(image,(QM) ((2*i*(
6821 GetPixelRed(image,n)
6822 -GetPixelRed(image,pixels))+m)
6824 GetPixelRed(image,pixels)),q);
6826 SetPixelGreen(image,(QM) ((2*i*(
6827 GetPixelGreen(image,n)
6828 -GetPixelGreen(image,pixels))+m)
6830 GetPixelGreen(image,pixels)),q);
6832 SetPixelBlue(image,(QM) ((2*i*(
6833 GetPixelBlue(image,n)
6834 -GetPixelBlue(image,pixels))+m)
6836 GetPixelBlue(image,pixels)),q);
6837 if (image->alpha_trait != UndefinedPixelTrait)
6838 SetPixelAlpha(image,(QM) ((2*i*(
6839 GetPixelAlpha(image,n)
6840 -GetPixelAlpha(image,pixels))+m)
6842 GetPixelAlpha(image,pixels)),q);
6845 if (magn_methx == 4)
6847 /* Replicate nearest */
6848 if (i <= ((m+1) << 1))
6850 SetPixelAlpha(image,
6851 GetPixelAlpha(image,pixels)+0,q);
6855 SetPixelAlpha(image,
6856 GetPixelAlpha(image,n)+0,q);
6861 else /* if (magn_methx == 3 || magn_methx == 5) */
6863 /* Replicate nearest */
6864 if (i <= ((m+1) << 1))
6866 SetPixelRed(image,GetPixelRed(image,pixels),q);
6867 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6868 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6869 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6874 SetPixelRed(image,GetPixelRed(image,n),q);
6875 SetPixelGreen(image,GetPixelGreen(image,n),q);
6876 SetPixelBlue(image,GetPixelBlue(image,n),q);
6877 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6880 if (magn_methx == 5)
6883 SetPixelAlpha(image,
6884 (QM) ((2*i*( GetPixelAlpha(image,n)
6885 -GetPixelAlpha(image,pixels))+m)/
6887 +GetPixelAlpha(image,pixels)),q);
6890 q+=GetPixelChannels(image);
6892 n+=GetPixelChannels(image);
6895 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6898 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6899 if (magn_methx != 1 || magn_methy != 1)
6902 Rescale pixels to Quantum
6904 for (y=0; y < (ssize_t) image->rows; y++)
6906 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6908 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6910 SetPixelRed(image,ScaleShortToQuantum(
6911 GetPixelRed(image,q)),q);
6912 SetPixelGreen(image,ScaleShortToQuantum(
6913 GetPixelGreen(image,q)),q);
6914 SetPixelBlue(image,ScaleShortToQuantum(
6915 GetPixelBlue(image,q)),q);
6916 SetPixelAlpha(image,ScaleShortToQuantum(
6917 GetPixelAlpha(image,q)),q);
6918 q+=GetPixelChannels(image);
6921 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6926 if (logging != MagickFalse)
6927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6928 " Finished MAGN processing");
6933 Crop_box is with respect to the upper left corner of the MNG.
6935 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6936 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6937 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6938 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6939 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6940 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6941 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6942 if ((crop_box.left != (mng_info->image_box.left
6943 +mng_info->x_off[object_id])) ||
6944 (crop_box.right != (mng_info->image_box.right
6945 +mng_info->x_off[object_id])) ||
6946 (crop_box.top != (mng_info->image_box.top
6947 +mng_info->y_off[object_id])) ||
6948 (crop_box.bottom != (mng_info->image_box.bottom
6949 +mng_info->y_off[object_id])))
6951 if (logging != MagickFalse)
6952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6953 " Crop the PNG image");
6955 if ((crop_box.left < crop_box.right) &&
6956 (crop_box.top < crop_box.bottom))
6965 Crop_info is with respect to the upper left corner of
6968 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6969 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6970 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6971 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6972 image->page.width=image->columns;
6973 image->page.height=image->rows;
6976 im=CropImage(image,&crop_info,exception);
6978 if (im != (Image *) NULL)
6980 image->columns=im->columns;
6981 image->rows=im->rows;
6982 im=DestroyImage(im);
6983 image->page.width=image->columns;
6984 image->page.height=image->rows;
6985 image->page.x=crop_box.left;
6986 image->page.y=crop_box.top;
6993 No pixels in crop area. The MNG spec still requires
6994 a layer, though, so make a single transparent pixel in
6995 the top left corner.
7000 (void) SetImageBackgroundColor(image,exception);
7001 image->page.width=1;
7002 image->page.height=1;
7007 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7008 image=mng_info->image;
7012 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7013 /* PNG does not handle depths greater than 16 so reduce it even
7016 if (image->depth > 16)
7020 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7021 if (image->depth > 8)
7023 /* To do: fill low byte properly */
7027 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7031 if (image_info->number_scenes != 0)
7033 if (mng_info->scenes_found >
7034 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7038 if (logging != MagickFalse)
7039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7040 " Finished reading image datastream.");
7042 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7044 (void) CloseBlob(image);
7046 if (logging != MagickFalse)
7047 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7048 " Finished reading all image datastreams.");
7050 #if defined(MNG_INSERT_LAYERS)
7051 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7052 (mng_info->mng_height))
7055 Insert a background layer if nothing else was found.
7057 if (logging != MagickFalse)
7058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7059 " No images found. Inserting a background layer.");
7061 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7064 Allocate next image structure.
7066 AcquireNextImage(image_info,image,exception);
7067 if (GetNextImageInList(image) == (Image *) NULL)
7069 image=DestroyImageList(image);
7070 MngInfoFreeStruct(mng_info,&have_mng_structure);
7072 if (logging != MagickFalse)
7073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7074 " Allocation failed, returning NULL.");
7076 return((Image *) NULL);
7078 image=SyncNextImageInList(image);
7080 image->columns=mng_info->mng_width;
7081 image->rows=mng_info->mng_height;
7082 image->page.width=mng_info->mng_width;
7083 image->page.height=mng_info->mng_height;
7086 image->background_color=mng_background_color;
7087 image->alpha_trait=UndefinedPixelTrait;
7089 if (image_info->ping == MagickFalse)
7090 (void) SetImageBackgroundColor(image,exception);
7092 mng_info->image_found++;
7095 image->iterations=mng_iterations;
7097 if (mng_iterations == 1)
7098 image->start_loop=MagickTrue;
7100 while (GetPreviousImageInList(image) != (Image *) NULL)
7103 if (image_count > 10*mng_info->image_found)
7105 if (logging != MagickFalse)
7106 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7108 (void) ThrowMagickException(exception,GetMagickModule(),
7109 CoderError,"Linked list is corrupted, beginning of list not found",
7110 "`%s'",image_info->filename);
7112 return((Image *) NULL);
7115 image=GetPreviousImageInList(image);
7117 if (GetNextImageInList(image) == (Image *) NULL)
7119 if (logging != MagickFalse)
7120 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7122 (void) ThrowMagickException(exception,GetMagickModule(),
7123 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7124 image_info->filename);
7128 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7129 GetNextImageInList(image) ==
7132 if (logging != MagickFalse)
7133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7134 " First image null");
7136 (void) ThrowMagickException(exception,GetMagickModule(),
7137 CoderError,"image->next for first image is NULL but shouldn't be.",
7138 "`%s'",image_info->filename);
7141 if (mng_info->image_found == 0)
7143 if (logging != MagickFalse)
7144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7145 " No visible images found.");
7147 (void) ThrowMagickException(exception,GetMagickModule(),
7148 CoderError,"No visible images in file","`%s'",image_info->filename);
7150 if (image != (Image *) NULL)
7151 image=DestroyImageList(image);
7153 MngInfoFreeStruct(mng_info,&have_mng_structure);
7154 return((Image *) NULL);
7157 if (mng_info->ticks_per_second)
7158 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7159 final_delay/mng_info->ticks_per_second;
7162 image->start_loop=MagickTrue;
7164 /* Find final nonzero image delay */
7165 final_image_delay=0;
7167 while (GetNextImageInList(image) != (Image *) NULL)
7170 final_image_delay=image->delay;
7172 image=GetNextImageInList(image);
7175 if (final_delay < final_image_delay)
7176 final_delay=final_image_delay;
7178 image->delay=final_delay;
7180 if (logging != MagickFalse)
7181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7182 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7183 (double) final_delay);
7185 if (logging != MagickFalse)
7191 image=GetFirstImageInList(image);
7193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7194 " Before coalesce:");
7196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7197 " scene 0 delay=%.20g",(double) image->delay);
7199 while (GetNextImageInList(image) != (Image *) NULL)
7201 image=GetNextImageInList(image);
7202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7203 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7207 image=GetFirstImageInList(image);
7208 #ifdef MNG_COALESCE_LAYERS
7218 if (logging != MagickFalse)
7219 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7222 next_image=CoalesceImages(image,exception);
7224 if (next_image == (Image *) NULL)
7225 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7227 image=DestroyImageList(image);
7230 for (next=image; next != (Image *) NULL; next=next_image)
7232 next->page.width=mng_info->mng_width;
7233 next->page.height=mng_info->mng_height;
7236 next->scene=scene++;
7237 next_image=GetNextImageInList(next);
7239 if (next_image == (Image *) NULL)
7242 if (next->delay == 0)
7245 next_image->previous=GetPreviousImageInList(next);
7246 if (GetPreviousImageInList(next) == (Image *) NULL)
7249 next->previous->next=next_image;
7250 next=DestroyImage(next);
7256 while (GetNextImageInList(image) != (Image *) NULL)
7257 image=GetNextImageInList(image);
7259 image->dispose=BackgroundDispose;
7261 if (logging != MagickFalse)
7267 image=GetFirstImageInList(image);
7269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7270 " After coalesce:");
7272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7273 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7274 (double) image->dispose);
7276 while (GetNextImageInList(image) != (Image *) NULL)
7278 image=GetNextImageInList(image);
7280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7281 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7282 (double) image->delay,(double) image->dispose);
7286 image=GetFirstImageInList(image);
7287 MngInfoFreeStruct(mng_info,&have_mng_structure);
7288 have_mng_structure=MagickFalse;
7290 if (logging != MagickFalse)
7291 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7293 return(GetFirstImageInList(image));
7295 #else /* PNG_LIBPNG_VER > 10011 */
7296 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7298 printf("Your PNG library is too old: You have libpng-%s\n",
7299 PNG_LIBPNG_VER_STRING);
7301 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7302 "PNG library is too old","`%s'",image_info->filename);
7304 return(Image *) NULL;
7307 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7309 return(ReadPNGImage(image_info,exception));
7311 #endif /* PNG_LIBPNG_VER > 10011 */
7315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7319 % R e g i s t e r P N G I m a g e %
7323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7325 % RegisterPNGImage() adds properties for the PNG image format to
7326 % the list of supported formats. The properties include the image format
7327 % tag, a method to read and/or write the format, whether the format
7328 % supports the saving of more than one frame to the same file or blob,
7329 % whether the format supports native in-memory I/O, and a brief
7330 % description of the format.
7332 % The format of the RegisterPNGImage method is:
7334 % size_t RegisterPNGImage(void)
7337 ModuleExport size_t RegisterPNGImage(void)
7340 version[MaxTextExtent];
7348 "See http://www.libpng.org/ for details about the PNG format."
7353 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7359 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7365 #if defined(PNG_LIBPNG_VER_STRING)
7366 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7367 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7369 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7371 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7372 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7377 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7378 entry->flags|=CoderSeekableStreamFlag; /* To do: eliminate this. */
7380 #if defined(MAGICKCORE_PNG_DELEGATE)
7381 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7382 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7385 entry->magick=(IsImageFormatHandler *) IsMNG;
7387 if (*version != '\0')
7388 entry->version=ConstantString(version);
7390 entry->mime_type=ConstantString("video/x-mng");
7391 entry->note=ConstantString(MNGNote);
7392 (void) RegisterMagickInfo(entry);
7394 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7396 #if defined(MAGICKCORE_PNG_DELEGATE)
7397 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7398 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7401 entry->magick=(IsImageFormatHandler *) IsPNG;
7402 entry->flags^=CoderAdjoinFlag;
7403 entry->mime_type=ConstantString("image/png");
7405 if (*version != '\0')
7406 entry->version=ConstantString(version);
7408 entry->note=ConstantString(PNGNote);
7409 (void) RegisterMagickInfo(entry);
7411 entry=AcquireMagickInfo("PNG","PNG8",
7412 "8-bit indexed with optional binary transparency");
7414 #if defined(MAGICKCORE_PNG_DELEGATE)
7415 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7416 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7419 entry->magick=(IsImageFormatHandler *) IsPNG;
7420 entry->flags^=CoderAdjoinFlag;
7421 entry->mime_type=ConstantString("image/png");
7422 (void) RegisterMagickInfo(entry);
7424 entry=AcquireMagickInfo("PNG","PNG24",
7425 "opaque or binary transparent 24-bit RGB");
7428 #if defined(ZLIB_VERSION)
7429 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7430 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7432 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7434 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7435 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7439 if (*version != '\0')
7440 entry->version=ConstantString(version);
7442 #if defined(MAGICKCORE_PNG_DELEGATE)
7443 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7444 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7447 entry->magick=(IsImageFormatHandler *) IsPNG;
7448 entry->flags^=CoderAdjoinFlag;
7449 entry->mime_type=ConstantString("image/png");
7450 (void) RegisterMagickInfo(entry);
7452 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7454 #if defined(MAGICKCORE_PNG_DELEGATE)
7455 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7456 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7459 entry->magick=(IsImageFormatHandler *) IsPNG;
7460 entry->flags^=CoderAdjoinFlag;
7461 entry->mime_type=ConstantString("image/png");
7462 (void) RegisterMagickInfo(entry);
7464 entry=AcquireMagickInfo("PNG","PNG48",
7465 "opaque or binary transparent 48-bit RGB");
7467 #if defined(MAGICKCORE_PNG_DELEGATE)
7468 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7469 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7472 entry->magick=(IsImageFormatHandler *) IsPNG;
7473 entry->flags^=CoderAdjoinFlag;
7474 entry->mime_type=ConstantString("image/png");
7475 (void) RegisterMagickInfo(entry);
7477 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7479 #if defined(MAGICKCORE_PNG_DELEGATE)
7480 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7481 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7484 entry->magick=(IsImageFormatHandler *) IsPNG;
7485 entry->flags^=CoderAdjoinFlag;
7486 entry->mime_type=ConstantString("image/png");
7487 (void) RegisterMagickInfo(entry);
7489 entry=AcquireMagickInfo("PNG","PNG00",
7490 "PNG inheriting bit-depth and color-type from original");
7492 #if defined(MAGICKCORE_PNG_DELEGATE)
7493 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7494 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7497 entry->magick=(IsImageFormatHandler *) IsPNG;
7498 entry->flags^=CoderAdjoinFlag;
7499 entry->mime_type=ConstantString("image/png");
7500 (void) RegisterMagickInfo(entry);
7502 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7504 #if defined(JNG_SUPPORTED)
7505 #if defined(MAGICKCORE_PNG_DELEGATE)
7506 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7507 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7511 entry->magick=(IsImageFormatHandler *) IsJNG;
7512 entry->flags^=CoderAdjoinFlag;
7513 entry->mime_type=ConstantString("image/x-jng");
7514 entry->note=ConstantString(JNGNote);
7515 (void) RegisterMagickInfo(entry);
7517 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7518 ping_semaphore=AcquireSemaphoreInfo();
7521 return(MagickImageCoderSignature);
7525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7529 % U n r e g i s t e r P N G I m a g e %
7533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7535 % UnregisterPNGImage() removes format registrations made by the
7536 % PNG module from the list of supported formats.
7538 % The format of the UnregisterPNGImage method is:
7540 % UnregisterPNGImage(void)
7543 ModuleExport void UnregisterPNGImage(void)
7545 (void) UnregisterMagickInfo("MNG");
7546 (void) UnregisterMagickInfo("PNG");
7547 (void) UnregisterMagickInfo("PNG8");
7548 (void) UnregisterMagickInfo("PNG24");
7549 (void) UnregisterMagickInfo("PNG32");
7550 (void) UnregisterMagickInfo("PNG48");
7551 (void) UnregisterMagickInfo("PNG64");
7552 (void) UnregisterMagickInfo("PNG00");
7553 (void) UnregisterMagickInfo("JNG");
7555 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7556 if (ping_semaphore != (SemaphoreInfo *) NULL)
7557 RelinquishSemaphoreInfo(&ping_semaphore);
7561 #if defined(MAGICKCORE_PNG_DELEGATE)
7562 #if PNG_LIBPNG_VER > 10011
7564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7568 % W r i t e M N G I m a g e %
7572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7574 % WriteMNGImage() writes an image in the Portable Network Graphics
7575 % Group's "Multiple-image Network Graphics" encoded image format.
7577 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7579 % The format of the WriteMNGImage method is:
7581 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7582 % Image *image,ExceptionInfo *exception)
7584 % A description of each parameter follows.
7586 % o image_info: the image info.
7588 % o image: The image.
7590 % o exception: return any errors or warnings in this structure.
7592 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7593 % "To do" under ReadPNGImage):
7595 % Preserve all unknown and not-yet-handled known chunks found in input
7596 % PNG file and copy them into output PNG files according to the PNG
7599 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7601 % Improve selection of color type (use indexed-colour or indexed-colour
7602 % with tRNS when 256 or fewer unique RGBA values are present).
7604 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7605 % This will be complicated if we limit ourselves to generating MNG-LC
7606 % files. For now we ignore disposal method 3 and simply overlay the next
7609 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7610 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7611 % [mostly done 15 June 1999 but still need to take care of tRNS]
7613 % Check for identical sRGB and replace with a global sRGB (and remove
7614 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7615 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7616 % local gAMA/cHRM with local sRGB if appropriate).
7618 % Check for identical sBIT chunks and write global ones.
7620 % Provide option to skip writing the signature tEXt chunks.
7622 % Use signatures to detect identical objects and reuse the first
7623 % instance of such objects instead of writing duplicate objects.
7625 % Use a smaller-than-32k value of compression window size when
7628 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7629 % ancillary text chunks and save profiles.
7631 % Provide an option to force LC files (to ensure exact framing rate)
7634 % Provide an option to force VLC files instead of LC, even when offsets
7635 % are present. This will involve expanding the embedded images with a
7636 % transparent region at the top and/or left.
7640 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7641 png_info *ping_info, unsigned char *profile_type, unsigned char
7642 *profile_description, unsigned char *profile_data, png_uint_32 length)
7661 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7663 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7666 if (image_info->verbose)
7668 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7669 (char *) profile_type, (double) length);
7672 #if PNG_LIBPNG_VER >= 10400
7673 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7675 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7677 description_length=(png_uint_32) strlen((const char *) profile_description);
7678 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7679 + description_length);
7680 #if PNG_LIBPNG_VER >= 10400
7681 text[0].text=(png_charp) png_malloc(ping,
7682 (png_alloc_size_t) allocated_length);
7683 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7685 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7686 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7688 text[0].key[0]='\0';
7689 (void) ConcatenateMagickString(text[0].key,
7690 "Raw profile type ",MaxTextExtent);
7691 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7695 (void) CopyMagickString(dp,(const char *) profile_description,
7697 dp+=description_length;
7699 (void) FormatLocaleString(dp,allocated_length-
7700 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7703 for (i=0; i < (ssize_t) length; i++)
7707 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7708 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7713 text[0].text_length=(png_size_t) (dp-text[0].text);
7714 text[0].compression=image_info->compression == NoCompression ||
7715 (image_info->compression == UndefinedCompression &&
7716 text[0].text_length < 128) ? -1 : 0;
7718 if (text[0].text_length <= allocated_length)
7719 png_set_text(ping,ping_info,text,1);
7721 png_free(ping,text[0].text);
7722 png_free(ping,text[0].key);
7723 png_free(ping,text);
7726 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7727 const char *string, MagickBooleanType logging)
7740 ResetImageProfileIterator(image);
7742 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7744 profile=GetImageProfile(image,name);
7746 if (profile != (const StringInfo *) NULL)
7751 if (LocaleNCompare(name,string,11) == 0)
7753 if (logging != MagickFalse)
7754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7755 " Found %s profile",name);
7757 ping_profile=CloneStringInfo(profile);
7758 data=GetStringInfoDatum(ping_profile),
7759 length=(png_uint_32) GetStringInfoLength(ping_profile);
7764 (void) WriteBlobMSBULong(image,length-5); /* data length */
7765 (void) WriteBlob(image,length-1,data+1);
7766 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7767 ping_profile=DestroyStringInfo(ping_profile);
7771 name=GetNextImageProfile(image);
7777 #if defined(PNG_tIME_SUPPORTED)
7778 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7779 const char *date,ExceptionInfo *exception)
7795 if (date != (const char *) NULL)
7797 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7800 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7801 "Invalid date format specified for png:tIME","`%s'",
7805 ptime.year=(png_uint_16) year;
7806 ptime.month=(png_byte) month;
7807 ptime.day=(png_byte) day;
7808 ptime.hour=(png_byte) hour;
7809 ptime.minute=(png_byte) minute;
7810 ptime.second=(png_byte) second;
7815 png_convert_from_time_t(&ptime,ttime);
7817 png_set_tIME(ping,info,&ptime);
7821 /* Write one PNG image */
7822 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7823 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7854 ping_trans_alpha[256];
7882 ping_have_cheap_transparency,
7895 /* ping_exclude_EXIF, */
7898 /* ping_exclude_iTXt, */
7904 /* ping_exclude_tRNS, */
7906 ping_exclude_zCCP, /* hex-encoded iCCP */
7909 ping_preserve_colormap,
7911 ping_need_colortype_warning,
7919 *volatile pixel_info;
7938 ping_interlace_method,
7939 ping_compression_method,
7956 number_semitransparent,
7958 ping_pHYs_unit_type;
7961 ping_pHYs_x_resolution,
7962 ping_pHYs_y_resolution;
7964 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7965 " Enter WriteOnePNGImage()");
7967 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7968 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7969 if (image_info == (ImageInfo *) NULL)
7970 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7972 /* Define these outside of the following "if logging()" block so they will
7973 * show in debuggers.
7976 (void) ConcatenateMagickString(im_vers,
7977 MagickLibVersionText,MaxTextExtent);
7978 (void) ConcatenateMagickString(im_vers,
7979 MagickLibAddendum,MaxTextExtent);
7982 (void) ConcatenateMagickString(libpng_vers,
7983 PNG_LIBPNG_VER_STRING,32);
7985 (void) ConcatenateMagickString(libpng_runv,
7986 png_get_libpng_ver(NULL),32);
7989 (void) ConcatenateMagickString(zlib_vers,
7992 (void) ConcatenateMagickString(zlib_runv,
7995 if (logging != MagickFalse)
7997 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
7999 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8001 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8003 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8006 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8008 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8010 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8015 /* Initialize some stuff */
8018 ping_interlace_method=0,
8019 ping_compression_method=0,
8020 ping_filter_method=0,
8023 ping_background.red = 0;
8024 ping_background.green = 0;
8025 ping_background.blue = 0;
8026 ping_background.gray = 0;
8027 ping_background.index = 0;
8029 ping_trans_color.red=0;
8030 ping_trans_color.green=0;
8031 ping_trans_color.blue=0;
8032 ping_trans_color.gray=0;
8034 ping_pHYs_unit_type = 0;
8035 ping_pHYs_x_resolution = 0;
8036 ping_pHYs_y_resolution = 0;
8038 ping_have_blob=MagickFalse;
8039 ping_have_cheap_transparency=MagickFalse;
8040 ping_have_color=MagickTrue;
8041 ping_have_non_bw=MagickTrue;
8042 ping_have_PLTE=MagickFalse;
8043 ping_have_bKGD=MagickFalse;
8044 ping_have_iCCP=MagickFalse;
8045 ping_have_pHYs=MagickFalse;
8046 ping_have_sRGB=MagickFalse;
8047 ping_have_tRNS=MagickFalse;
8049 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8050 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8051 ping_exclude_date=mng_info->ping_exclude_date;
8052 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8053 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8054 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8055 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8056 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8057 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8058 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8059 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8060 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8061 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8062 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8063 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8064 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8066 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8067 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8068 ping_need_colortype_warning = MagickFalse;
8070 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8071 * i.e., eliminate the ICC profile and set image->rendering_intent.
8072 * Note that this will not involve any changes to the actual pixels
8073 * but merely passes information to applications that read the resulting
8076 * To do: recognize other variants of the sRGB profile, using the CRC to
8077 * verify all recognized variants including the 7 already known.
8079 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8081 * Use something other than image->rendering_intent to record the fact
8082 * that the sRGB profile was found.
8084 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8085 * profile. Record the Blackpoint Compensation, if any.
8087 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8095 ResetImageProfileIterator(image);
8096 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8098 profile=GetImageProfile(image,name);
8100 if (profile != (StringInfo *) NULL)
8102 if ((LocaleCompare(name,"ICC") == 0) ||
8103 (LocaleCompare(name,"ICM") == 0))
8118 length=(png_uint_32) GetStringInfoLength(profile);
8120 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8122 if (length == sRGB_info[icheck].len)
8126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8127 " Got a %lu-byte ICC profile (potentially sRGB)",
8128 (unsigned long) length);
8130 data=GetStringInfoDatum(profile);
8131 profile_crc=crc32(0,data,length);
8133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8134 " with crc=%8x",(unsigned int) profile_crc);
8138 if (profile_crc == sRGB_info[icheck].crc)
8140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8141 " It is sRGB with rendering intent = %s",
8142 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8143 sRGB_info[icheck].intent));
8144 if (image->rendering_intent==UndefinedIntent)
8146 image->rendering_intent=
8147 Magick_RenderingIntent_from_PNG_RenderingIntent(
8148 sRGB_info[icheck].intent);
8150 ping_exclude_iCCP = MagickTrue;
8151 ping_exclude_zCCP = MagickTrue;
8152 ping_have_sRGB = MagickTrue;
8157 if (sRGB_info[icheck].len == 0)
8158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8159 " Got a %lu-byte ICC profile not recognized as sRGB",
8160 (unsigned long) length);
8163 name=GetNextImageProfile(image);
8168 number_semitransparent = 0;
8169 number_transparent = 0;
8171 if (logging != MagickFalse)
8173 if (image->storage_class == UndefinedClass)
8174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8175 " image->storage_class=UndefinedClass");
8176 if (image->storage_class == DirectClass)
8177 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8178 " image->storage_class=DirectClass");
8179 if (image->storage_class == PseudoClass)
8180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8181 " image->storage_class=PseudoClass");
8182 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8183 " image->taint=MagickTrue":
8184 " image->taint=MagickFalse");
8187 if (image->storage_class == PseudoClass &&
8188 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8189 mng_info->write_png48 || mng_info->write_png64 ||
8190 (mng_info->write_png_colortype != 1 &&
8191 mng_info->write_png_colortype != 5)))
8193 (void) SyncImage(image,exception);
8194 image->storage_class = DirectClass;
8197 if (ping_preserve_colormap == MagickFalse)
8199 if (image->storage_class != PseudoClass && image->colormap != NULL)
8201 /* Free the bogus colormap; it can cause trouble later */
8202 if (logging != MagickFalse)
8203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8204 " Freeing bogus colormap");
8205 (void) RelinquishMagickMemory(image->colormap);
8206 image->colormap=NULL;
8210 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8211 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8214 Sometimes we get PseudoClass images whose RGB values don't match
8215 the colors in the colormap. This code syncs the RGB values.
8217 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8218 (void) SyncImage(image,exception);
8220 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8221 if (image->depth > 8)
8223 if (logging != MagickFalse)
8224 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8225 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8231 /* Respect the -depth option */
8232 if (image->depth < 4)
8237 if (image->depth > 2)
8239 /* Scale to 4-bit */
8240 LBR04PacketRGBO(image->background_color);
8242 for (y=0; y < (ssize_t) image->rows; y++)
8244 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8246 if (r == (Quantum *) NULL)
8249 for (x=0; x < (ssize_t) image->columns; x++)
8252 r+=GetPixelChannels(image);
8255 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8259 if (image->storage_class == PseudoClass && image->colormap != NULL)
8261 for (i=0; i < (ssize_t) image->colors; i++)
8263 LBR04PacketRGBO(image->colormap[i]);
8267 else if (image->depth > 1)
8269 /* Scale to 2-bit */
8270 LBR02PacketRGBO(image->background_color);
8272 for (y=0; y < (ssize_t) image->rows; y++)
8274 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8276 if (r == (Quantum *) NULL)
8279 for (x=0; x < (ssize_t) image->columns; x++)
8282 r+=GetPixelChannels(image);
8285 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8289 if (image->storage_class == PseudoClass && image->colormap != NULL)
8291 for (i=0; i < (ssize_t) image->colors; i++)
8293 LBR02PacketRGBO(image->colormap[i]);
8299 /* Scale to 1-bit */
8300 LBR01PacketRGBO(image->background_color);
8302 for (y=0; y < (ssize_t) image->rows; y++)
8304 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8306 if (r == (Quantum *) NULL)
8309 for (x=0; x < (ssize_t) image->columns; x++)
8312 r+=GetPixelChannels(image);
8315 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8319 if (image->storage_class == PseudoClass && image->colormap != NULL)
8321 for (i=0; i < (ssize_t) image->colors; i++)
8323 LBR01PacketRGBO(image->colormap[i]);
8329 /* To do: set to next higher multiple of 8 */
8330 if (image->depth < 8)
8333 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8334 /* PNG does not handle depths greater than 16 so reduce it even
8337 if (image->depth > 8)
8341 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8342 if (image->depth > 8)
8344 /* To do: fill low byte properly */
8348 if (image->depth == 16 && mng_info->write_png_depth != 16)
8349 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8353 image_colors = (int) image->colors;
8354 number_opaque = (int) image->colors;
8355 number_transparent = 0;
8356 number_semitransparent = 0;
8358 if (mng_info->write_png_colortype &&
8359 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8360 mng_info->write_png_colortype < 4 &&
8361 image->alpha_trait == UndefinedPixelTrait)))
8363 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8364 * are not going to need the result.
8366 if (mng_info->write_png_colortype == 1 ||
8367 mng_info->write_png_colortype == 5)
8368 ping_have_color=MagickFalse;
8370 if (image->alpha_trait != UndefinedPixelTrait)
8372 number_transparent = 2;
8373 number_semitransparent = 1;
8377 if (mng_info->write_png_colortype < 7)
8381 * Normally we run this just once, but in the case of writing PNG8
8382 * we reduce the transparency to binary and run again, then if there
8383 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8384 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8385 * palette. Then (To do) we take care of a final reduction that is only
8386 * needed if there are still 256 colors present and one of them has both
8387 * transparent and opaque instances.
8390 tried_332 = MagickFalse;
8391 tried_333 = MagickFalse;
8392 tried_444 = MagickFalse;
8397 * Sometimes we get DirectClass images that have 256 colors or fewer.
8398 * This code will build a colormap.
8400 * Also, sometimes we get PseudoClass images with an out-of-date
8401 * colormap. This code will replace the colormap with a new one.
8402 * Sometimes we get PseudoClass images that have more than 256 colors.
8403 * This code will delete the colormap and change the image to
8406 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8407 * even though it sometimes contains left-over non-opaque values.
8409 * Also we gather some information (number of opaque, transparent,
8410 * and semitransparent pixels, and whether the image has any non-gray
8411 * pixels or only black-and-white pixels) that we might need later.
8413 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8414 * we need to check for bogus non-opaque values, at least.
8422 semitransparent[260],
8425 register const Quantum
8432 if (logging != MagickFalse)
8433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8434 " Enter BUILD_PALETTE:");
8436 if (logging != MagickFalse)
8438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439 " image->columns=%.20g",(double) image->columns);
8440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8441 " image->rows=%.20g",(double) image->rows);
8442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8443 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8445 " image->depth=%.20g",(double) image->depth);
8447 if (image->storage_class == PseudoClass && image->colormap != NULL)
8449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8450 " Original colormap:");
8451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8452 " i (red,green,blue,alpha)");
8454 for (i=0; i < 256; i++)
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " %d (%d,%d,%d,%d)",
8459 (int) image->colormap[i].red,
8460 (int) image->colormap[i].green,
8461 (int) image->colormap[i].blue,
8462 (int) image->colormap[i].alpha);
8465 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8470 " %d (%d,%d,%d,%d)",
8472 (int) image->colormap[i].red,
8473 (int) image->colormap[i].green,
8474 (int) image->colormap[i].blue,
8475 (int) image->colormap[i].alpha);
8480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8481 " image->colors=%d",(int) image->colors);
8483 if (image->colors == 0)
8484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8485 " (zero means unknown)");
8487 if (ping_preserve_colormap == MagickFalse)
8488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8489 " Regenerate the colormap");
8494 number_semitransparent = 0;
8495 number_transparent = 0;
8497 for (y=0; y < (ssize_t) image->rows; y++)
8499 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8501 if (q == (Quantum *) NULL)
8504 for (x=0; x < (ssize_t) image->columns; x++)
8506 if (image->alpha_trait == UndefinedPixelTrait ||
8507 GetPixelAlpha(image,q) == OpaqueAlpha)
8509 if (number_opaque < 259)
8511 if (number_opaque == 0)
8513 GetPixelInfoPixel(image, q, opaque);
8514 opaque[0].alpha=OpaqueAlpha;
8518 for (i=0; i< (ssize_t) number_opaque; i++)
8520 if (IsPixelEquivalent(image,q, opaque+i))
8524 if (i == (ssize_t) number_opaque && number_opaque < 259)
8527 GetPixelInfoPixel(image, q, opaque+i);
8528 opaque[i].alpha=OpaqueAlpha;
8532 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8534 if (number_transparent < 259)
8536 if (number_transparent == 0)
8538 GetPixelInfoPixel(image, q, transparent);
8539 ping_trans_color.red=(unsigned short)
8540 GetPixelRed(image,q);
8541 ping_trans_color.green=(unsigned short)
8542 GetPixelGreen(image,q);
8543 ping_trans_color.blue=(unsigned short)
8544 GetPixelBlue(image,q);
8545 ping_trans_color.gray=(unsigned short)
8546 GetPixelGray(image,q);
8547 number_transparent = 1;
8550 for (i=0; i< (ssize_t) number_transparent; i++)
8552 if (IsPixelEquivalent(image,q, transparent+i))
8556 if (i == (ssize_t) number_transparent &&
8557 number_transparent < 259)
8559 number_transparent++;
8560 GetPixelInfoPixel(image,q,transparent+i);
8566 if (number_semitransparent < 259)
8568 if (number_semitransparent == 0)
8570 GetPixelInfoPixel(image,q,semitransparent);
8571 number_semitransparent = 1;
8574 for (i=0; i< (ssize_t) number_semitransparent; i++)
8576 if (IsPixelEquivalent(image,q, semitransparent+i)
8577 && GetPixelAlpha(image,q) ==
8578 semitransparent[i].alpha)
8582 if (i == (ssize_t) number_semitransparent &&
8583 number_semitransparent < 259)
8585 number_semitransparent++;
8586 GetPixelInfoPixel(image, q, semitransparent+i);
8590 q+=GetPixelChannels(image);
8594 if (mng_info->write_png8 == MagickFalse &&
8595 ping_exclude_bKGD == MagickFalse)
8597 /* Add the background color to the palette, if it
8598 * isn't already there.
8600 if (logging != MagickFalse)
8602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8603 " Check colormap for background (%d,%d,%d)",
8604 (int) image->background_color.red,
8605 (int) image->background_color.green,
8606 (int) image->background_color.blue);
8608 for (i=0; i<number_opaque; i++)
8610 if (opaque[i].red == image->background_color.red &&
8611 opaque[i].green == image->background_color.green &&
8612 opaque[i].blue == image->background_color.blue)
8615 if (number_opaque < 259 && i == number_opaque)
8617 opaque[i] = image->background_color;
8618 ping_background.index = i;
8620 if (logging != MagickFalse)
8622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8623 " background_color index is %d",(int) i);
8627 else if (logging != MagickFalse)
8628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8629 " No room in the colormap to add background color");
8632 image_colors=number_opaque+number_transparent+number_semitransparent;
8634 if (logging != MagickFalse)
8636 if (image_colors > 256)
8637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8638 " image has more than 256 colors");
8641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642 " image has %d colors",image_colors);
8645 if (ping_preserve_colormap != MagickFalse)
8648 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8650 ping_have_color=MagickFalse;
8651 ping_have_non_bw=MagickFalse;
8653 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8656 "incompatible colorspace");
8657 ping_have_color=MagickTrue;
8658 ping_have_non_bw=MagickTrue;
8661 if(image_colors > 256)
8663 for (y=0; y < (ssize_t) image->rows; y++)
8665 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8667 if (q == (Quantum *) NULL)
8671 for (x=0; x < (ssize_t) image->columns; x++)
8673 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8674 GetPixelRed(image,s) != GetPixelBlue(image,s))
8676 ping_have_color=MagickTrue;
8677 ping_have_non_bw=MagickTrue;
8680 s+=GetPixelChannels(image);
8683 if (ping_have_color != MagickFalse)
8686 /* Worst case is black-and-white; we are looking at every
8690 if (ping_have_non_bw == MagickFalse)
8693 for (x=0; x < (ssize_t) image->columns; x++)
8695 if (GetPixelRed(image,s) != 0 &&
8696 GetPixelRed(image,s) != QuantumRange)
8698 ping_have_non_bw=MagickTrue;
8701 s+=GetPixelChannels(image);
8708 if (image_colors < 257)
8714 * Initialize image colormap.
8717 if (logging != MagickFalse)
8718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8719 " Sort the new colormap");
8721 /* Sort palette, transparent first */;
8725 for (i=0; i<number_transparent; i++)
8726 colormap[n++] = transparent[i];
8728 for (i=0; i<number_semitransparent; i++)
8729 colormap[n++] = semitransparent[i];
8731 for (i=0; i<number_opaque; i++)
8732 colormap[n++] = opaque[i];
8734 ping_background.index +=
8735 (number_transparent + number_semitransparent);
8737 /* image_colors < 257; search the colormap instead of the pixels
8738 * to get ping_have_color and ping_have_non_bw
8742 if (ping_have_color == MagickFalse)
8744 if (colormap[i].red != colormap[i].green ||
8745 colormap[i].red != colormap[i].blue)
8747 ping_have_color=MagickTrue;
8748 ping_have_non_bw=MagickTrue;
8753 if (ping_have_non_bw == MagickFalse)
8755 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8756 ping_have_non_bw=MagickTrue;
8760 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8761 (number_transparent == 0 && number_semitransparent == 0)) &&
8762 (((mng_info->write_png_colortype-1) ==
8763 PNG_COLOR_TYPE_PALETTE) ||
8764 (mng_info->write_png_colortype == 0)))
8766 if (logging != MagickFalse)
8768 if (n != (ssize_t) image_colors)
8769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8770 " image_colors (%d) and n (%d) don't match",
8773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8774 " AcquireImageColormap");
8777 image->colors = image_colors;
8779 if (AcquireImageColormap(image,image_colors,exception) ==
8781 ThrowWriterException(ResourceLimitError,
8782 "MemoryAllocationFailed");
8784 for (i=0; i< (ssize_t) image_colors; i++)
8785 image->colormap[i] = colormap[i];
8787 if (logging != MagickFalse)
8789 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8790 " image->colors=%d (%d)",
8791 (int) image->colors, image_colors);
8793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8794 " Update the pixel indexes");
8797 /* Sync the pixel indices with the new colormap */
8799 for (y=0; y < (ssize_t) image->rows; y++)
8801 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8803 if (q == (Quantum *) NULL)
8806 for (x=0; x < (ssize_t) image->columns; x++)
8808 for (i=0; i< (ssize_t) image_colors; i++)
8810 if ((image->alpha_trait == UndefinedPixelTrait ||
8811 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8812 image->colormap[i].red == GetPixelRed(image,q) &&
8813 image->colormap[i].green == GetPixelGreen(image,q) &&
8814 image->colormap[i].blue == GetPixelBlue(image,q))
8816 SetPixelIndex(image,i,q);
8820 q+=GetPixelChannels(image);
8823 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8829 if (logging != MagickFalse)
8831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8832 " image->colors=%d", (int) image->colors);
8834 if (image->colormap != NULL)
8836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8837 " i (red,green,blue,alpha)");
8839 for (i=0; i < (ssize_t) image->colors; i++)
8841 if (i < 300 || i >= (ssize_t) image->colors - 10)
8843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8844 " %d (%d,%d,%d,%d)",
8846 (int) image->colormap[i].red,
8847 (int) image->colormap[i].green,
8848 (int) image->colormap[i].blue,
8849 (int) image->colormap[i].alpha);
8854 if (number_transparent < 257)
8855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8856 " number_transparent = %d",
8857 number_transparent);
8860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8861 " number_transparent > 256");
8863 if (number_opaque < 257)
8864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8865 " number_opaque = %d",
8869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8870 " number_opaque > 256");
8872 if (number_semitransparent < 257)
8873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8874 " number_semitransparent = %d",
8875 number_semitransparent);
8878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8879 " number_semitransparent > 256");
8881 if (ping_have_non_bw == MagickFalse)
8882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8883 " All pixels and the background are black or white");
8885 else if (ping_have_color == MagickFalse)
8886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8887 " All pixels and the background are gray");
8890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8891 " At least one pixel or the background is non-gray");
8893 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8894 " Exit BUILD_PALETTE:");
8897 if (mng_info->write_png8 == MagickFalse)
8900 /* Make any reductions necessary for the PNG8 format */
8901 if (image_colors <= 256 &&
8902 image_colors != 0 && image->colormap != NULL &&
8903 number_semitransparent == 0 &&
8904 number_transparent <= 1)
8907 /* PNG8 can't have semitransparent colors so we threshold the
8908 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8909 * transparent color so if more than one is transparent we merge
8910 * them into image->background_color.
8912 if (number_semitransparent != 0 || number_transparent > 1)
8914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8915 " Thresholding the alpha channel to binary");
8917 for (y=0; y < (ssize_t) image->rows; y++)
8919 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8921 if (r == (Quantum *) NULL)
8924 for (x=0; x < (ssize_t) image->columns; x++)
8926 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8928 SetPixelViaPixelInfo(image,&image->background_color,r);
8929 SetPixelAlpha(image,TransparentAlpha,r);
8932 SetPixelAlpha(image,OpaqueAlpha,r);
8933 r+=GetPixelChannels(image);
8936 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8939 if (image_colors != 0 && image_colors <= 256 &&
8940 image->colormap != NULL)
8941 for (i=0; i<image_colors; i++)
8942 image->colormap[i].alpha =
8943 (image->colormap[i].alpha > TransparentAlpha/2 ?
8944 TransparentAlpha : OpaqueAlpha);
8949 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8950 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8951 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8954 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8956 if (logging != MagickFalse)
8957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8958 " Quantizing the background color to 4-4-4");
8960 tried_444 = MagickTrue;
8962 LBR04PacketRGB(image->background_color);
8964 if (logging != MagickFalse)
8965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8966 " Quantizing the pixel colors to 4-4-4");
8968 if (image->colormap == NULL)
8970 for (y=0; y < (ssize_t) image->rows; y++)
8972 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8974 if (r == (Quantum *) NULL)
8977 for (x=0; x < (ssize_t) image->columns; x++)
8979 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8981 r+=GetPixelChannels(image);
8984 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8989 else /* Should not reach this; colormap already exists and
8992 if (logging != MagickFalse)
8993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8994 " Quantizing the colormap to 4-4-4");
8996 for (i=0; i<image_colors; i++)
8998 LBR04PacketRGB(image->colormap[i]);
9004 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9006 if (logging != MagickFalse)
9007 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9008 " Quantizing the background color to 3-3-3");
9010 tried_333 = MagickTrue;
9012 LBR03PacketRGB(image->background_color);
9014 if (logging != MagickFalse)
9015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016 " Quantizing the pixel colors to 3-3-3-1");
9018 if (image->colormap == NULL)
9020 for (y=0; y < (ssize_t) image->rows; y++)
9022 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9024 if (r == (Quantum *) NULL)
9027 for (x=0; x < (ssize_t) image->columns; x++)
9029 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9031 r+=GetPixelChannels(image);
9034 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9039 else /* Should not reach this; colormap already exists and
9042 if (logging != MagickFalse)
9043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044 " Quantizing the colormap to 3-3-3-1");
9045 for (i=0; i<image_colors; i++)
9047 LBR03PacketRGB(image->colormap[i]);
9053 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9055 if (logging != MagickFalse)
9056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9057 " Quantizing the background color to 3-3-2");
9059 tried_332 = MagickTrue;
9061 /* Red and green were already done so we only quantize the blue
9065 LBR02PacketBlue(image->background_color);
9067 if (logging != MagickFalse)
9068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9069 " Quantizing the pixel colors to 3-3-2-1");
9071 if (image->colormap == NULL)
9073 for (y=0; y < (ssize_t) image->rows; y++)
9075 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9077 if (r == (Quantum *) NULL)
9080 for (x=0; x < (ssize_t) image->columns; x++)
9082 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9084 r+=GetPixelChannels(image);
9087 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9092 else /* Should not reach this; colormap already exists and
9095 if (logging != MagickFalse)
9096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9097 " Quantizing the colormap to 3-3-2-1");
9098 for (i=0; i<image_colors; i++)
9100 LBR02PacketBlue(image->colormap[i]);
9106 if (image_colors == 0 || image_colors > 256)
9108 /* Take care of special case with 256 opaque colors + 1 transparent
9109 * color. We don't need to quantize to 2-3-2-1; we only need to
9110 * eliminate one color, so we'll merge the two darkest red
9111 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9113 if (logging != MagickFalse)
9114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115 " Merging two dark red background colors to 3-3-2-1");
9117 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9118 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9119 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9121 image->background_color.red=ScaleCharToQuantum(0x24);
9124 if (logging != MagickFalse)
9125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9126 " Merging two dark red pixel colors to 3-3-2-1");
9128 if (image->colormap == NULL)
9130 for (y=0; y < (ssize_t) image->rows; y++)
9132 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9134 if (r == (Quantum *) NULL)
9137 for (x=0; x < (ssize_t) image->columns; x++)
9139 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9140 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9141 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9142 GetPixelAlpha(image,r) == OpaqueAlpha)
9144 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9146 r+=GetPixelChannels(image);
9149 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9157 for (i=0; i<image_colors; i++)
9159 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9160 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9161 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9163 image->colormap[i].red=ScaleCharToQuantum(0x24);
9170 /* END OF BUILD_PALETTE */
9172 /* If we are excluding the tRNS chunk and there is transparency,
9173 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9176 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9177 (number_transparent != 0 || number_semitransparent != 0))
9179 unsigned int colortype=mng_info->write_png_colortype;
9181 if (ping_have_color == MagickFalse)
9182 mng_info->write_png_colortype = 5;
9185 mng_info->write_png_colortype = 7;
9187 if (colortype != 0 &&
9188 mng_info->write_png_colortype != colortype)
9189 ping_need_colortype_warning=MagickTrue;
9193 /* See if cheap transparency is possible. It is only possible
9194 * when there is a single transparent color, no semitransparent
9195 * color, and no opaque color that has the same RGB components
9196 * as the transparent color. We only need this information if
9197 * we are writing a PNG with colortype 0 or 2, and we have not
9198 * excluded the tRNS chunk.
9200 if (number_transparent == 1 &&
9201 mng_info->write_png_colortype < 4)
9203 ping_have_cheap_transparency = MagickTrue;
9205 if (number_semitransparent != 0)
9206 ping_have_cheap_transparency = MagickFalse;
9208 else if (image_colors == 0 || image_colors > 256 ||
9209 image->colormap == NULL)
9211 register const Quantum
9214 for (y=0; y < (ssize_t) image->rows; y++)
9216 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9218 if (q == (Quantum *) NULL)
9221 for (x=0; x < (ssize_t) image->columns; x++)
9223 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9224 (unsigned short) GetPixelRed(image,q) ==
9225 ping_trans_color.red &&
9226 (unsigned short) GetPixelGreen(image,q) ==
9227 ping_trans_color.green &&
9228 (unsigned short) GetPixelBlue(image,q) ==
9229 ping_trans_color.blue)
9231 ping_have_cheap_transparency = MagickFalse;
9235 q+=GetPixelChannels(image);
9238 if (ping_have_cheap_transparency == MagickFalse)
9244 /* Assuming that image->colormap[0] is the one transparent color
9245 * and that all others are opaque.
9247 if (image_colors > 1)
9248 for (i=1; i<image_colors; i++)
9249 if (image->colormap[i].red == image->colormap[0].red &&
9250 image->colormap[i].green == image->colormap[0].green &&
9251 image->colormap[i].blue == image->colormap[0].blue)
9253 ping_have_cheap_transparency = MagickFalse;
9258 if (logging != MagickFalse)
9260 if (ping_have_cheap_transparency == MagickFalse)
9261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9262 " Cheap transparency is not possible.");
9265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9266 " Cheap transparency is possible.");
9270 ping_have_cheap_transparency = MagickFalse;
9272 image_depth=image->depth;
9274 quantum_info = (QuantumInfo *) NULL;
9276 image_colors=(int) image->colors;
9277 image_matte=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse;
9279 if (mng_info->write_png_colortype < 5)
9280 mng_info->IsPalette=image->storage_class == PseudoClass &&
9281 image_colors <= 256 && image->colormap != NULL;
9283 mng_info->IsPalette = MagickFalse;
9285 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9286 (image->colors == 0 || image->colormap == NULL))
9288 image_info=DestroyImageInfo(image_info);
9289 image=DestroyImage(image);
9290 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9291 "Cannot write PNG8 or color-type 3; colormap is NULL",
9292 "`%s'",IMimage->filename);
9293 return(MagickFalse);
9297 Allocate the PNG structures
9299 #ifdef PNG_USER_MEM_SUPPORTED
9300 error_info.image=image;
9301 error_info.exception=exception;
9302 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9303 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9304 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9307 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9308 MagickPNGErrorHandler,MagickPNGWarningHandler);
9311 if (ping == (png_struct *) NULL)
9312 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9314 ping_info=png_create_info_struct(ping);
9316 if (ping_info == (png_info *) NULL)
9318 png_destroy_write_struct(&ping,(png_info **) NULL);
9319 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9322 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9323 pixel_info=(MemoryInfo *) NULL;
9325 if (setjmp(png_jmpbuf(ping)))
9331 if (image_info->verbose)
9332 (void) printf("PNG write has failed.\n");
9334 png_destroy_write_struct(&ping,&ping_info);
9335 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9336 UnlockSemaphoreInfo(ping_semaphore);
9339 if (pixel_info != (MemoryInfo *) NULL)
9340 pixel_info=RelinquishVirtualMemory(pixel_info);
9342 if (quantum_info != (QuantumInfo *) NULL)
9343 quantum_info=DestroyQuantumInfo(quantum_info);
9345 if (ping_have_blob != MagickFalse)
9346 (void) CloseBlob(image);
9347 image_info=DestroyImageInfo(image_info);
9348 image=DestroyImage(image);
9349 return(MagickFalse);
9352 /* { For navigation to end of SETJMP-protected block. Within this
9353 * block, use png_error() instead of Throwing an Exception, to ensure
9354 * that libpng is able to clean up, and that the semaphore is unlocked.
9357 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9358 LockSemaphoreInfo(ping_semaphore);
9361 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9362 /* Allow benign errors */
9363 png_set_benign_errors(ping, 1);
9367 Prepare PNG for writing.
9370 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9371 if (mng_info->write_mng)
9373 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9374 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9375 /* Disable new libpng-1.5.10 feature when writing a MNG because
9376 * zero-length PLTE is OK
9378 png_set_check_for_invalid_index (ping, 0);
9383 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9384 if (mng_info->write_mng)
9385 png_permit_empty_plte(ping,MagickTrue);
9392 ping_width=(png_uint_32) image->columns;
9393 ping_height=(png_uint_32) image->rows;
9395 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9398 if (mng_info->write_png48 || mng_info->write_png64)
9401 if (mng_info->write_png_depth != 0)
9402 image_depth=mng_info->write_png_depth;
9404 /* Adjust requested depth to next higher valid depth if necessary */
9405 if (image_depth > 8)
9408 if ((image_depth > 4) && (image_depth < 8))
9411 if (image_depth == 3)
9414 if (logging != MagickFalse)
9416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9417 " width=%.20g",(double) ping_width);
9418 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9419 " height=%.20g",(double) ping_height);
9420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9421 " image_matte=%.20g",(double) image->alpha_trait);
9422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9423 " image->depth=%.20g",(double) image->depth);
9424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9425 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9428 save_image_depth=image_depth;
9429 ping_bit_depth=(png_byte) save_image_depth;
9432 #if defined(PNG_pHYs_SUPPORTED)
9433 if (ping_exclude_pHYs == MagickFalse)
9435 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9436 (!mng_info->write_mng || !mng_info->equal_physs))
9438 if (logging != MagickFalse)
9439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9440 " Setting up pHYs chunk");
9442 if (image->units == PixelsPerInchResolution)
9444 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9445 ping_pHYs_x_resolution=
9446 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9447 ping_pHYs_y_resolution=
9448 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9451 else if (image->units == PixelsPerCentimeterResolution)
9453 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9454 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9455 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9460 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9461 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9462 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9465 if (logging != MagickFalse)
9466 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9467 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9468 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9469 (int) ping_pHYs_unit_type);
9470 ping_have_pHYs = MagickTrue;
9475 if (ping_exclude_bKGD == MagickFalse)
9477 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9483 if (ping_bit_depth == 8)
9486 if (ping_bit_depth == 4)
9489 if (ping_bit_depth == 2)
9492 if (ping_bit_depth == 1)
9495 ping_background.red=(png_uint_16)
9496 (ScaleQuantumToShort(image->background_color.red) & mask);
9498 ping_background.green=(png_uint_16)
9499 (ScaleQuantumToShort(image->background_color.green) & mask);
9501 ping_background.blue=(png_uint_16)
9502 (ScaleQuantumToShort(image->background_color.blue) & mask);
9504 ping_background.gray=(png_uint_16) ping_background.green;
9507 if (logging != MagickFalse)
9509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9510 " Setting up bKGD chunk (1)");
9511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9512 " background_color index is %d",
9513 (int) ping_background.index);
9515 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9516 " ping_bit_depth=%d",ping_bit_depth);
9519 ping_have_bKGD = MagickTrue;
9523 Select the color type.
9528 if (mng_info->IsPalette && mng_info->write_png8)
9530 /* To do: make this a function cause it's used twice, except
9531 for reducing the sample depth from 8. */
9533 number_colors=image_colors;
9535 ping_have_tRNS=MagickFalse;
9540 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9542 if (logging != MagickFalse)
9543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9544 " Setting up PLTE chunk with %d colors (%d)",
9545 number_colors, image_colors);
9547 for (i=0; i < (ssize_t) number_colors; i++)
9549 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9550 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9551 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9552 if (logging != MagickFalse)
9553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9554 #if MAGICKCORE_QUANTUM_DEPTH == 8
9555 " %3ld (%3d,%3d,%3d)",
9557 " %5ld (%5d,%5d,%5d)",
9559 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9563 ping_have_PLTE=MagickTrue;
9564 image_depth=ping_bit_depth;
9567 if (matte != MagickFalse)
9570 Identify which colormap entry is transparent.
9572 assert(number_colors <= 256);
9573 assert(image->colormap != NULL);
9575 for (i=0; i < (ssize_t) number_transparent; i++)
9576 ping_trans_alpha[i]=0;
9579 ping_num_trans=(unsigned short) (number_transparent +
9580 number_semitransparent);
9582 if (ping_num_trans == 0)
9583 ping_have_tRNS=MagickFalse;
9586 ping_have_tRNS=MagickTrue;
9589 if (ping_exclude_bKGD == MagickFalse)
9592 * Identify which colormap entry is the background color.
9595 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9596 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9599 ping_background.index=(png_byte) i;
9601 if (logging != MagickFalse)
9603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604 " background_color index is %d",
9605 (int) ping_background.index);
9608 } /* end of write_png8 */
9610 else if (mng_info->write_png_colortype == 1)
9612 image_matte=MagickFalse;
9613 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9616 else if (mng_info->write_png24 || mng_info->write_png48 ||
9617 mng_info->write_png_colortype == 3)
9619 image_matte=MagickFalse;
9620 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9623 else if (mng_info->write_png32 || mng_info->write_png64 ||
9624 mng_info->write_png_colortype == 7)
9626 image_matte=MagickTrue;
9627 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9630 else /* mng_info->write_pngNN not specified */
9632 image_depth=ping_bit_depth;
9634 if (mng_info->write_png_colortype != 0)
9636 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9638 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9639 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9640 image_matte=MagickTrue;
9643 image_matte=MagickFalse;
9645 if (logging != MagickFalse)
9646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9647 " PNG colortype %d was specified:",(int) ping_color_type);
9650 else /* write_png_colortype not specified */
9652 if (logging != MagickFalse)
9653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9654 " Selecting PNG colortype:");
9656 ping_color_type=(png_byte) ((matte != MagickFalse)?
9657 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9659 if (image_info->type == TrueColorType)
9661 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9662 image_matte=MagickFalse;
9665 if (image_info->type == TrueColorAlphaType)
9667 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9668 image_matte=MagickTrue;
9671 if (image_info->type == PaletteType ||
9672 image_info->type == PaletteAlphaType)
9673 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9675 if (mng_info->write_png_colortype == 0 &&
9676 image_info->type == UndefinedType)
9678 if (ping_have_color == MagickFalse)
9680 if (image_matte == MagickFalse)
9682 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9683 image_matte=MagickFalse;
9688 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9689 image_matte=MagickTrue;
9694 if (image_matte == MagickFalse)
9696 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9697 image_matte=MagickFalse;
9702 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9703 image_matte=MagickTrue;
9710 if (logging != MagickFalse)
9711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9712 " Selected PNG colortype=%d",ping_color_type);
9714 if (ping_bit_depth < 8)
9716 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9717 ping_color_type == PNG_COLOR_TYPE_RGB ||
9718 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9722 old_bit_depth=ping_bit_depth;
9724 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9726 if (image->alpha_trait == UndefinedPixelTrait && ping_have_non_bw == MagickFalse)
9730 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9735 if (image->colors == 0)
9738 png_error(ping,"image has 0 colors");
9741 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9742 ping_bit_depth <<= 1;
9745 if (logging != MagickFalse)
9747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9748 " Number of colors: %.20g",(double) image_colors);
9750 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9751 " Tentative PNG bit depth: %d",ping_bit_depth);
9754 if (ping_bit_depth < (int) mng_info->write_png_depth)
9755 ping_bit_depth = mng_info->write_png_depth;
9758 image_depth=ping_bit_depth;
9760 if (logging != MagickFalse)
9762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9763 " Tentative PNG color type: %s (%.20g)",
9764 PngColorTypeToString(ping_color_type),
9765 (double) ping_color_type);
9767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9768 " image_info->type: %.20g",(double) image_info->type);
9770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9771 " image_depth: %.20g",(double) image_depth);
9773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9775 " image->depth: %.20g",(double) image->depth);
9777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9778 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9781 if (matte != MagickFalse)
9783 if (mng_info->IsPalette)
9785 if (mng_info->write_png_colortype == 0)
9787 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9789 if (ping_have_color != MagickFalse)
9790 ping_color_type=PNG_COLOR_TYPE_RGBA;
9794 * Determine if there is any transparent color.
9796 if (number_transparent + number_semitransparent == 0)
9799 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9802 image_matte=MagickFalse;
9804 if (mng_info->write_png_colortype == 0)
9805 ping_color_type&=0x03;
9815 if (ping_bit_depth == 8)
9818 if (ping_bit_depth == 4)
9821 if (ping_bit_depth == 2)
9824 if (ping_bit_depth == 1)
9827 ping_trans_color.red=(png_uint_16)
9828 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9830 ping_trans_color.green=(png_uint_16)
9831 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9833 ping_trans_color.blue=(png_uint_16)
9834 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9836 ping_trans_color.gray=(png_uint_16)
9837 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
9838 image->colormap)) & mask);
9840 ping_trans_color.index=(png_byte) 0;
9842 ping_have_tRNS=MagickTrue;
9845 if (ping_have_tRNS != MagickFalse)
9848 * Determine if there is one and only one transparent color
9849 * and if so if it is fully transparent.
9851 if (ping_have_cheap_transparency == MagickFalse)
9852 ping_have_tRNS=MagickFalse;
9855 if (ping_have_tRNS != MagickFalse)
9857 if (mng_info->write_png_colortype == 0)
9858 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9860 if (image_depth == 8)
9862 ping_trans_color.red&=0xff;
9863 ping_trans_color.green&=0xff;
9864 ping_trans_color.blue&=0xff;
9865 ping_trans_color.gray&=0xff;
9871 if (image_depth == 8)
9873 ping_trans_color.red&=0xff;
9874 ping_trans_color.green&=0xff;
9875 ping_trans_color.blue&=0xff;
9876 ping_trans_color.gray&=0xff;
9883 if (ping_have_tRNS != MagickFalse)
9884 image_matte=MagickFalse;
9886 if ((mng_info->IsPalette) &&
9887 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9888 ping_have_color == MagickFalse &&
9889 (image_matte == MagickFalse || image_depth >= 8))
9893 if (image_matte != MagickFalse)
9894 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9896 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9898 ping_color_type=PNG_COLOR_TYPE_GRAY;
9900 if (save_image_depth == 16 && image_depth == 8)
9902 if (logging != MagickFalse)
9904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9905 " Scaling ping_trans_color (0)");
9907 ping_trans_color.gray*=0x0101;
9911 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9912 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9914 if ((image_colors == 0) ||
9915 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9916 image_colors=(int) (one << image_depth);
9918 if (image_depth > 8)
9924 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9926 if(!mng_info->write_png_depth)
9930 while ((int) (one << ping_bit_depth)
9931 < (ssize_t) image_colors)
9932 ping_bit_depth <<= 1;
9936 else if (ping_color_type ==
9937 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9938 mng_info->IsPalette)
9940 /* Check if grayscale is reducible */
9943 depth_4_ok=MagickTrue,
9944 depth_2_ok=MagickTrue,
9945 depth_1_ok=MagickTrue;
9947 for (i=0; i < (ssize_t) image_colors; i++)
9952 intensity=ScaleQuantumToChar(image->colormap[i].red);
9954 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9955 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9956 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9957 depth_2_ok=depth_1_ok=MagickFalse;
9958 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9959 depth_1_ok=MagickFalse;
9962 if (depth_1_ok && mng_info->write_png_depth <= 1)
9965 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9968 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9973 image_depth=ping_bit_depth;
9978 if (mng_info->IsPalette)
9980 number_colors=image_colors;
9982 if (image_depth <= 8)
9987 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9989 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
9991 for (i=0; i < (ssize_t) number_colors; i++)
9993 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9994 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9995 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9998 if (logging != MagickFalse)
9999 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10000 " Setting up PLTE chunk with %d colors",
10003 ping_have_PLTE=MagickTrue;
10006 /* color_type is PNG_COLOR_TYPE_PALETTE */
10007 if (mng_info->write_png_depth == 0)
10015 while ((one << ping_bit_depth) < (size_t) number_colors)
10016 ping_bit_depth <<= 1;
10021 if (matte != MagickFalse)
10024 * Set up trans_colors array.
10026 assert(number_colors <= 256);
10028 ping_num_trans=(unsigned short) (number_transparent +
10029 number_semitransparent);
10031 if (ping_num_trans == 0)
10032 ping_have_tRNS=MagickFalse;
10036 if (logging != MagickFalse)
10038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039 " Scaling ping_trans_color (1)");
10041 ping_have_tRNS=MagickTrue;
10043 for (i=0; i < ping_num_trans; i++)
10045 ping_trans_alpha[i]= (png_byte)
10046 ScaleQuantumToChar(image->colormap[i].alpha);
10056 if (image_depth < 8)
10059 if ((save_image_depth == 16) && (image_depth == 8))
10061 if (logging != MagickFalse)
10063 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10064 " Scaling ping_trans_color from (%d,%d,%d)",
10065 (int) ping_trans_color.red,
10066 (int) ping_trans_color.green,
10067 (int) ping_trans_color.blue);
10070 ping_trans_color.red*=0x0101;
10071 ping_trans_color.green*=0x0101;
10072 ping_trans_color.blue*=0x0101;
10073 ping_trans_color.gray*=0x0101;
10075 if (logging != MagickFalse)
10077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10079 (int) ping_trans_color.red,
10080 (int) ping_trans_color.green,
10081 (int) ping_trans_color.blue);
10086 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10087 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10090 Adjust background and transparency samples in sub-8-bit grayscale files.
10092 if (ping_bit_depth < 8 && ping_color_type ==
10093 PNG_COLOR_TYPE_GRAY)
10101 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10103 if (ping_exclude_bKGD == MagickFalse)
10106 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10107 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10108 &image->background_color))) +.5)));
10110 if (logging != MagickFalse)
10111 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10112 " Setting up bKGD chunk (2)");
10113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10114 " background_color index is %d",
10115 (int) ping_background.index);
10117 ping_have_bKGD = MagickTrue;
10120 if (logging != MagickFalse)
10121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10122 " Scaling ping_trans_color.gray from %d",
10123 (int)ping_trans_color.gray);
10125 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10126 ping_trans_color.gray)+.5);
10128 if (logging != MagickFalse)
10129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10130 " to %d", (int)ping_trans_color.gray);
10133 if (ping_exclude_bKGD == MagickFalse)
10135 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10138 Identify which colormap entry is the background color.
10141 number_colors=image_colors;
10143 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10144 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10147 ping_background.index=(png_byte) i;
10149 if (logging != MagickFalse)
10151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10152 " Setting up bKGD chunk with index=%d",(int) i);
10155 if (i < (ssize_t) number_colors)
10157 ping_have_bKGD = MagickTrue;
10159 if (logging != MagickFalse)
10161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10162 " background =(%d,%d,%d)",
10163 (int) ping_background.red,
10164 (int) ping_background.green,
10165 (int) ping_background.blue);
10169 else /* Can't happen */
10171 if (logging != MagickFalse)
10172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10173 " No room in PLTE to add bKGD color");
10174 ping_have_bKGD = MagickFalse;
10179 if (logging != MagickFalse)
10180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10184 Initialize compression level and filtering.
10186 if (logging != MagickFalse)
10188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10189 " Setting up deflate compression");
10191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10192 " Compression buffer size: 32768");
10195 png_set_compression_buffer_size(ping,32768L);
10197 if (logging != MagickFalse)
10198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199 " Compression mem level: 9");
10201 png_set_compression_mem_level(ping, 9);
10203 /* Untangle the "-quality" setting:
10205 Undefined is 0; the default is used.
10210 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10211 zlib default compression level
10213 1-9: the zlib compression level
10217 0-4: the PNG filter method
10219 5: libpng adaptive filtering if compression level > 5
10220 libpng filter type "none" if compression level <= 5
10221 or if image is grayscale or palette
10223 6: libpng adaptive filtering
10225 7: "LOCO" filtering (intrapixel differing) if writing
10226 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10227 and earlier because of a missing "else".
10229 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10230 filtering. Unused prior to IM-6.7.0-10, was same as 6
10232 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10233 Unused prior to IM-6.7.0-10, was same as 6
10235 Note that using the -quality option, not all combinations of
10236 PNG filter type, zlib compression level, and zlib compression
10237 strategy are possible. This will be addressed soon in a
10238 release that accomodates "-define png:compression-strategy", etc.
10242 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10243 image_info->quality;
10247 if (mng_info->write_png_compression_strategy == 0)
10248 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10251 else if (mng_info->write_png_compression_level == 0)
10256 level=(int) MagickMin((ssize_t) quality/10,9);
10258 mng_info->write_png_compression_level = level+1;
10261 if (mng_info->write_png_compression_strategy == 0)
10263 if ((quality %10) == 8 || (quality %10) == 9)
10264 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10265 mng_info->write_png_compression_strategy=Z_RLE+1;
10267 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10271 if (mng_info->write_png_compression_filter == 0)
10272 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10274 if (logging != MagickFalse)
10276 if (mng_info->write_png_compression_level)
10277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10278 " Compression level: %d",
10279 (int) mng_info->write_png_compression_level-1);
10281 if (mng_info->write_png_compression_strategy)
10282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10283 " Compression strategy: %d",
10284 (int) mng_info->write_png_compression_strategy-1);
10286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10287 " Setting up filtering");
10289 if (mng_info->write_png_compression_filter == 6)
10290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10291 " Base filter method: ADAPTIVE");
10292 else if (mng_info->write_png_compression_filter == 0 ||
10293 mng_info->write_png_compression_filter == 1)
10294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10295 " Base filter method: NONE");
10297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10298 " Base filter method: %d",
10299 (int) mng_info->write_png_compression_filter-1);
10302 if (mng_info->write_png_compression_level != 0)
10303 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10305 if (mng_info->write_png_compression_filter == 6)
10307 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10308 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10310 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10312 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10314 else if (mng_info->write_png_compression_filter == 7 ||
10315 mng_info->write_png_compression_filter == 10)
10316 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10318 else if (mng_info->write_png_compression_filter == 8)
10320 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10321 if (mng_info->write_mng)
10323 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10324 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10325 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10328 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10331 else if (mng_info->write_png_compression_filter == 9)
10332 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10334 else if (mng_info->write_png_compression_filter != 0)
10335 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10336 mng_info->write_png_compression_filter-1);
10338 if (mng_info->write_png_compression_strategy != 0)
10339 png_set_compression_strategy(ping,
10340 mng_info->write_png_compression_strategy-1);
10342 ping_interlace_method=image_info->interlace != NoInterlace;
10344 if (mng_info->write_mng)
10345 png_set_sig_bytes(ping,8);
10347 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10349 if (mng_info->write_png_colortype != 0)
10351 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10352 if (ping_have_color != MagickFalse)
10354 ping_color_type = PNG_COLOR_TYPE_RGB;
10356 if (ping_bit_depth < 8)
10360 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10361 if (ping_have_color != MagickFalse)
10362 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10365 if (ping_need_colortype_warning != MagickFalse ||
10366 ((mng_info->write_png_depth &&
10367 (int) mng_info->write_png_depth != ping_bit_depth) ||
10368 (mng_info->write_png_colortype &&
10369 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10370 mng_info->write_png_colortype != 7 &&
10371 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10373 if (logging != MagickFalse)
10375 if (ping_need_colortype_warning != MagickFalse)
10377 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10378 " Image has transparency but tRNS chunk was excluded");
10381 if (mng_info->write_png_depth)
10383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384 " Defined png:bit-depth=%u, Computed depth=%u",
10385 mng_info->write_png_depth,
10389 if (mng_info->write_png_colortype)
10391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10392 " Defined png:color-type=%u, Computed color type=%u",
10393 mng_info->write_png_colortype-1,
10399 "Cannot write image with defined png:bit-depth or png:color-type.");
10402 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10404 /* Add an opaque matte channel */
10405 image->alpha_trait = BlendPixelTrait;
10406 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10408 if (logging != MagickFalse)
10409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10410 " Added an opaque matte channel");
10413 if (number_transparent != 0 || number_semitransparent != 0)
10415 if (ping_color_type < 4)
10417 ping_have_tRNS=MagickTrue;
10418 if (logging != MagickFalse)
10419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10420 " Setting ping_have_tRNS=MagickTrue.");
10424 if (logging != MagickFalse)
10425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10426 " Writing PNG header chunks");
10428 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10429 ping_bit_depth,ping_color_type,
10430 ping_interlace_method,ping_compression_method,
10431 ping_filter_method);
10433 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10435 png_set_PLTE(ping,ping_info,palette,number_colors);
10437 if (logging != MagickFalse)
10439 for (i=0; i< (ssize_t) number_colors; i++)
10441 if (i < ping_num_trans)
10442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10443 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10445 (int) palette[i].red,
10446 (int) palette[i].green,
10447 (int) palette[i].blue,
10449 (int) ping_trans_alpha[i]);
10451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10452 " PLTE[%d] = (%d,%d,%d)",
10454 (int) palette[i].red,
10455 (int) palette[i].green,
10456 (int) palette[i].blue);
10461 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10462 if (ping_exclude_sRGB != MagickFalse ||
10463 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10465 if ((ping_exclude_tEXt == MagickFalse ||
10466 ping_exclude_zTXt == MagickFalse) &&
10467 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10469 ResetImageProfileIterator(image);
10470 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10472 profile=GetImageProfile(image,name);
10474 if (profile != (StringInfo *) NULL)
10476 #ifdef PNG_WRITE_iCCP_SUPPORTED
10477 if ((LocaleCompare(name,"ICC") == 0) ||
10478 (LocaleCompare(name,"ICM") == 0))
10481 if (ping_exclude_iCCP == MagickFalse)
10483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10484 " Setting up iCCP chunk");
10486 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10487 #if (PNG_LIBPNG_VER < 10500)
10488 (png_charp) GetStringInfoDatum(profile),
10490 (png_const_bytep) GetStringInfoDatum(profile),
10492 (png_uint_32) GetStringInfoLength(profile));
10493 ping_have_iCCP = MagickTrue;
10499 if (ping_exclude_zCCP == MagickFalse)
10501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10502 " Setting up zTXT chunk with uuencoded ICC");
10503 Magick_png_write_raw_profile(image_info,ping,ping_info,
10504 (unsigned char *) name,(unsigned char *) name,
10505 GetStringInfoDatum(profile),
10506 (png_uint_32) GetStringInfoLength(profile));
10507 ping_have_iCCP = MagickTrue;
10511 if (logging != MagickFalse)
10512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10513 " Setting up text chunk with %s profile",name);
10515 name=GetNextImageProfile(image);
10520 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10521 if ((mng_info->have_write_global_srgb == 0) &&
10522 ping_have_iCCP != MagickTrue &&
10523 (ping_have_sRGB != MagickFalse ||
10524 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10526 if (ping_exclude_sRGB == MagickFalse)
10529 Note image rendering intent.
10531 if (logging != MagickFalse)
10532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10533 " Setting up sRGB chunk");
10535 (void) png_set_sRGB(ping,ping_info,(
10536 Magick_RenderingIntent_to_PNG_RenderingIntent(
10537 image->rendering_intent)));
10539 ping_have_sRGB = MagickTrue;
10543 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10546 if (ping_exclude_gAMA == MagickFalse &&
10547 ping_have_iCCP == MagickFalse &&
10548 ping_have_sRGB == MagickFalse &&
10549 (ping_exclude_sRGB == MagickFalse ||
10550 (image->gamma < .45 || image->gamma > .46)))
10552 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10556 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10558 if (logging != MagickFalse)
10559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10560 " Setting up gAMA chunk");
10562 png_set_gAMA(ping,ping_info,image->gamma);
10566 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10568 if ((mng_info->have_write_global_chrm == 0) &&
10569 (image->chromaticity.red_primary.x != 0.0))
10572 Note image chromaticity.
10573 Note: if cHRM+gAMA == sRGB write sRGB instead.
10581 wp=image->chromaticity.white_point;
10582 rp=image->chromaticity.red_primary;
10583 gp=image->chromaticity.green_primary;
10584 bp=image->chromaticity.blue_primary;
10586 if (logging != MagickFalse)
10587 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10588 " Setting up cHRM chunk");
10590 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10596 if (ping_exclude_bKGD == MagickFalse)
10598 if (ping_have_bKGD != MagickFalse)
10600 png_set_bKGD(ping,ping_info,&ping_background);
10601 if (logging != MagickFalse)
10603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10604 " Setting up bKGD chunk");
10605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10606 " background color = (%d,%d,%d)",
10607 (int) ping_background.red,
10608 (int) ping_background.green,
10609 (int) ping_background.blue);
10610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10611 " index = %d, gray=%d",
10612 (int) ping_background.index,
10613 (int) ping_background.gray);
10618 if (ping_exclude_pHYs == MagickFalse)
10620 if (ping_have_pHYs != MagickFalse)
10622 png_set_pHYs(ping,ping_info,
10623 ping_pHYs_x_resolution,
10624 ping_pHYs_y_resolution,
10625 ping_pHYs_unit_type);
10627 if (logging != MagickFalse)
10629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10630 " Setting up pHYs chunk");
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " x_resolution=%lu",
10633 (unsigned long) ping_pHYs_x_resolution);
10634 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10635 " y_resolution=%lu",
10636 (unsigned long) ping_pHYs_y_resolution);
10637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639 (unsigned long) ping_pHYs_unit_type);
10644 #if defined(PNG_oFFs_SUPPORTED)
10645 if (ping_exclude_oFFs == MagickFalse)
10647 if (image->page.x || image->page.y)
10649 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10650 (png_int_32) image->page.y, 0);
10652 if (logging != MagickFalse)
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10655 (int) image->page.x, (int) image->page.y);
10660 #if defined(PNG_tIME_SUPPORTED)
10661 if (ping_exclude_tIME == MagickFalse)
10666 if (image->taint == MagickFalse)
10668 timestamp=GetImageOption(image_info,"png:tIME");
10670 if (timestamp == (const char *) NULL)
10671 timestamp=GetImageProperty(image,"png:tIME",exception);
10676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10677 " Reset tIME in tainted image");
10679 timestamp=GetImageProperty(image,"date:modify",exception);
10682 if (timestamp != (const char *) NULL)
10683 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10687 if (mng_info->need_blob != MagickFalse)
10689 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10691 png_error(ping,"WriteBlob Failed");
10693 ping_have_blob=MagickTrue;
10696 png_write_info_before_PLTE(ping, ping_info);
10698 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10700 if (logging != MagickFalse)
10702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10703 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10706 if (ping_color_type == 3)
10707 (void) png_set_tRNS(ping, ping_info,
10714 (void) png_set_tRNS(ping, ping_info,
10717 &ping_trans_color);
10719 if (logging != MagickFalse)
10721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10722 " tRNS color =(%d,%d,%d)",
10723 (int) ping_trans_color.red,
10724 (int) ping_trans_color.green,
10725 (int) ping_trans_color.blue);
10730 /* write any png-chunk-b profiles */
10731 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10733 png_write_info(ping,ping_info);
10735 /* write any PNG-chunk-m profiles */
10736 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10738 if (ping_exclude_vpAg == MagickFalse)
10740 if ((image->page.width != 0 && image->page.width != image->columns) ||
10741 (image->page.height != 0 && image->page.height != image->rows))
10746 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10747 PNGType(chunk,mng_vpAg);
10748 LogPNGChunk(logging,mng_vpAg,9L);
10749 PNGLong(chunk+4,(png_uint_32) image->page.width);
10750 PNGLong(chunk+8,(png_uint_32) image->page.height);
10751 chunk[12]=0; /* unit = pixels */
10752 (void) WriteBlob(image,13,chunk);
10753 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10757 #if (PNG_LIBPNG_VER == 10206)
10758 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10759 #define PNG_HAVE_IDAT 0x04
10760 ping->mode |= PNG_HAVE_IDAT;
10761 #undef PNG_HAVE_IDAT
10764 png_set_packing(ping);
10768 rowbytes=image->columns;
10769 if (image_depth > 8)
10771 switch (ping_color_type)
10773 case PNG_COLOR_TYPE_RGB:
10777 case PNG_COLOR_TYPE_GRAY_ALPHA:
10781 case PNG_COLOR_TYPE_RGBA:
10789 if (logging != MagickFalse)
10791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10792 " Writing PNG image data");
10794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10795 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10797 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10798 if (pixel_info == (MemoryInfo *) NULL)
10799 png_error(ping,"Allocation of memory for pixels failed");
10800 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10803 Initialize image scanlines.
10805 quantum_info=AcquireQuantumInfo(image_info,image);
10806 if (quantum_info == (QuantumInfo *) NULL)
10807 png_error(ping,"Memory allocation for quantum_info failed");
10808 quantum_info->format=UndefinedQuantumFormat;
10809 quantum_info->depth=image_depth;
10810 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10811 num_passes=png_set_interlace_handling(ping);
10813 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10814 !mng_info->write_png48 && !mng_info->write_png64 &&
10815 !mng_info->write_png32) &&
10816 (mng_info->IsPalette ||
10817 (image_info->type == BilevelType)) &&
10818 image_matte == MagickFalse &&
10819 ping_have_non_bw == MagickFalse)
10821 /* Palette, Bilevel, or Opaque Monochrome */
10822 register const Quantum
10825 quantum_info->depth=8;
10826 for (pass=0; pass < num_passes; pass++)
10829 Convert PseudoClass image to a PNG monochrome image.
10831 for (y=0; y < (ssize_t) image->rows; y++)
10833 if (logging != MagickFalse && y == 0)
10834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10835 " Writing row of pixels (0)");
10837 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10839 if (p == (const Quantum *) NULL)
10842 if (mng_info->IsPalette)
10844 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10845 quantum_info,GrayQuantum,ping_pixels,exception);
10846 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10847 mng_info->write_png_depth &&
10848 mng_info->write_png_depth != old_bit_depth)
10850 /* Undo pixel scaling */
10851 for (i=0; i < (ssize_t) image->columns; i++)
10852 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10853 >> (8-old_bit_depth));
10859 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10860 quantum_info,RedQuantum,ping_pixels,exception);
10863 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10864 for (i=0; i < (ssize_t) image->columns; i++)
10865 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10868 if (logging != MagickFalse && y == 0)
10869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10870 " Writing row of pixels (1)");
10872 png_write_row(ping,ping_pixels);
10874 status=SetImageProgress(image,LoadImageTag,
10875 (MagickOffsetType) (pass * image->rows + y),
10876 num_passes * image->rows);
10878 if (status == MagickFalse)
10884 else /* Not Palette, Bilevel, or Opaque Monochrome */
10886 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10887 !mng_info->write_png48 && !mng_info->write_png64 &&
10888 !mng_info->write_png32) && (image_matte != MagickFalse ||
10889 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10890 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10892 register const Quantum
10895 for (pass=0; pass < num_passes; pass++)
10898 for (y=0; y < (ssize_t) image->rows; y++)
10900 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10902 if (p == (const Quantum *) NULL)
10905 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10907 if (mng_info->IsPalette)
10908 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10909 quantum_info,GrayQuantum,ping_pixels,exception);
10912 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10913 quantum_info,RedQuantum,ping_pixels,exception);
10915 if (logging != MagickFalse && y == 0)
10916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10917 " Writing GRAY PNG pixels (2)");
10920 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10922 if (logging != MagickFalse && y == 0)
10923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10924 " Writing GRAY_ALPHA PNG pixels (2)");
10926 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10927 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10930 if (logging != MagickFalse && y == 0)
10931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932 " Writing row of pixels (2)");
10934 png_write_row(ping,ping_pixels);
10936 status=SetImageProgress(image,LoadImageTag,
10937 (MagickOffsetType) (pass * image->rows + y),
10938 num_passes * image->rows);
10940 if (status == MagickFalse)
10948 register const Quantum
10951 for (pass=0; pass < num_passes; pass++)
10953 if ((image_depth > 8) ||
10954 mng_info->write_png24 ||
10955 mng_info->write_png32 ||
10956 mng_info->write_png48 ||
10957 mng_info->write_png64 ||
10958 (!mng_info->write_png8 && !mng_info->IsPalette))
10960 for (y=0; y < (ssize_t) image->rows; y++)
10962 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10964 if (p == (const Quantum *) NULL)
10967 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10969 if (image->storage_class == DirectClass)
10970 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10971 quantum_info,RedQuantum,ping_pixels,exception);
10974 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10975 quantum_info,GrayQuantum,ping_pixels,exception);
10978 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10980 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10981 quantum_info,GrayAlphaQuantum,ping_pixels,
10984 if (logging != MagickFalse && y == 0)
10985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10986 " Writing GRAY_ALPHA PNG pixels (3)");
10989 else if (image_matte != MagickFalse)
10990 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10991 quantum_info,RGBAQuantum,ping_pixels,exception);
10994 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10995 quantum_info,RGBQuantum,ping_pixels,exception);
10997 if (logging != MagickFalse && y == 0)
10998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10999 " Writing row of pixels (3)");
11001 png_write_row(ping,ping_pixels);
11003 status=SetImageProgress(image,LoadImageTag,
11004 (MagickOffsetType) (pass * image->rows + y),
11005 num_passes * image->rows);
11007 if (status == MagickFalse)
11013 /* not ((image_depth > 8) ||
11014 mng_info->write_png24 || mng_info->write_png32 ||
11015 mng_info->write_png48 || mng_info->write_png64 ||
11016 (!mng_info->write_png8 && !mng_info->IsPalette))
11019 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11020 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11022 if (logging != MagickFalse)
11023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11024 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11026 quantum_info->depth=8;
11030 for (y=0; y < (ssize_t) image->rows; y++)
11032 if (logging != MagickFalse && y == 0)
11033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11034 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11036 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11038 if (p == (const Quantum *) NULL)
11041 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11043 quantum_info->depth=image->depth;
11045 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11046 quantum_info,GrayQuantum,ping_pixels,exception);
11049 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11051 if (logging != MagickFalse && y == 0)
11052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11053 " Writing GRAY_ALPHA PNG pixels (4)");
11055 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11056 quantum_info,GrayAlphaQuantum,ping_pixels,
11062 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11063 quantum_info,IndexQuantum,ping_pixels,exception);
11065 if (logging != MagickFalse && y <= 2)
11067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11068 " Writing row of non-gray pixels (4)");
11070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11071 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11072 (int)ping_pixels[0],(int)ping_pixels[1]);
11075 png_write_row(ping,ping_pixels);
11077 status=SetImageProgress(image,LoadImageTag,
11078 (MagickOffsetType) (pass * image->rows + y),
11079 num_passes * image->rows);
11081 if (status == MagickFalse)
11089 if (quantum_info != (QuantumInfo *) NULL)
11090 quantum_info=DestroyQuantumInfo(quantum_info);
11092 if (logging != MagickFalse)
11094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11095 " Wrote PNG image data");
11097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11098 " Width: %.20g",(double) ping_width);
11100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11101 " Height: %.20g",(double) ping_height);
11103 if (mng_info->write_png_depth)
11105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11106 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11110 " PNG bit-depth written: %d",ping_bit_depth);
11112 if (mng_info->write_png_colortype)
11114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11115 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11119 " PNG color-type written: %d",ping_color_type);
11121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11122 " PNG Interlace method: %d",ping_interlace_method);
11125 Generate text chunks after IDAT.
11127 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11129 ResetImagePropertyIterator(image);
11130 property=GetNextImageProperty(image);
11131 while (property != (const char *) NULL)
11136 value=GetImageProperty(image,property,exception);
11138 /* Don't write any "png:" or "jpeg:" properties; those are just for
11139 * "identify" or for passing through to another JPEG
11141 if ((LocaleNCompare(property,"png:",4) != 0 &&
11142 LocaleNCompare(property,"jpeg:",5) != 0) &&
11145 /* Suppress density and units if we wrote a pHYs chunk */
11146 (ping_exclude_pHYs != MagickFalse ||
11147 LocaleCompare(property,"density") != 0 ||
11148 LocaleCompare(property,"units") != 0) &&
11150 /* Suppress the IM-generated Date:create and Date:modify */
11151 (ping_exclude_date == MagickFalse ||
11152 LocaleNCompare(property, "Date:",5) != 0))
11154 if (value != (const char *) NULL)
11157 #if PNG_LIBPNG_VER >= 10400
11158 text=(png_textp) png_malloc(ping,
11159 (png_alloc_size_t) sizeof(png_text));
11161 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11163 text[0].key=(char *) property;
11164 text[0].text=(char *) value;
11165 text[0].text_length=strlen(value);
11167 if (ping_exclude_tEXt != MagickFalse)
11168 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11170 else if (ping_exclude_zTXt != MagickFalse)
11171 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11175 text[0].compression=image_info->compression == NoCompression ||
11176 (image_info->compression == UndefinedCompression &&
11177 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11178 PNG_TEXT_COMPRESSION_zTXt ;
11181 if (logging != MagickFalse)
11183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11184 " Setting up text chunk");
11186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187 " keyword: '%s'",text[0].key);
11190 png_set_text(ping,ping_info,text,1);
11191 png_free(ping,text);
11194 property=GetNextImageProperty(image);
11198 /* write any PNG-chunk-e profiles */
11199 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11201 if (logging != MagickFalse)
11202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11203 " Writing PNG end info");
11205 png_write_end(ping,ping_info);
11207 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11209 if (mng_info->page.x || mng_info->page.y ||
11210 (ping_width != mng_info->page.width) ||
11211 (ping_height != mng_info->page.height))
11217 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11219 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11220 PNGType(chunk,mng_FRAM);
11221 LogPNGChunk(logging,mng_FRAM,27L);
11223 chunk[5]=0; /* frame name separator (no name) */
11224 chunk[6]=1; /* flag for changing delay, for next frame only */
11225 chunk[7]=0; /* flag for changing frame timeout */
11226 chunk[8]=1; /* flag for changing frame clipping for next frame */
11227 chunk[9]=0; /* flag for changing frame sync_id */
11228 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11229 chunk[14]=0; /* clipping boundaries delta type */
11230 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11232 (png_uint_32) (mng_info->page.x + ping_width));
11233 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11235 (png_uint_32) (mng_info->page.y + ping_height));
11236 (void) WriteBlob(image,31,chunk);
11237 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11238 mng_info->old_framing_mode=4;
11239 mng_info->framing_mode=1;
11243 mng_info->framing_mode=3;
11245 if (mng_info->write_mng && !mng_info->need_fram &&
11246 ((int) image->dispose == 3))
11247 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11250 Free PNG resources.
11253 png_destroy_write_struct(&ping,&ping_info);
11255 pixel_info=RelinquishVirtualMemory(pixel_info);
11257 if (ping_have_blob != MagickFalse)
11258 (void) CloseBlob(image);
11260 image_info=DestroyImageInfo(image_info);
11261 image=DestroyImage(image);
11263 /* Store bit depth actually written */
11264 s[0]=(char) ping_bit_depth;
11267 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11269 if (logging != MagickFalse)
11270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11271 " exit WriteOnePNGImage()");
11273 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11274 UnlockSemaphoreInfo(ping_semaphore);
11277 /* } for navigation to beginning of SETJMP-protected block. Revert to
11278 * Throwing an Exception when an error occurs.
11281 return(MagickTrue);
11282 /* End write one PNG image */
11287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11291 % W r i t e P N G I m a g e %
11295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11297 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11298 % Multiple-image Network Graphics (MNG) image file.
11300 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11302 % The format of the WritePNGImage method is:
11304 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11305 % Image *image,ExceptionInfo *exception)
11307 % A description of each parameter follows:
11309 % o image_info: the image info.
11311 % o image: The image.
11313 % o exception: return any errors or warnings in this structure.
11315 % Returns MagickTrue on success, MagickFalse on failure.
11317 % Communicating with the PNG encoder:
11319 % While the datastream written is always in PNG format and normally would
11320 % be given the "png" file extension, this method also writes the following
11321 % pseudo-formats which are subsets of png:
11323 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11324 % a depth greater than 8, the depth is reduced. If transparency
11325 % is present, the tRNS chunk must only have values 0 and 255
11326 % (i.e., transparency is binary: fully opaque or fully
11327 % transparent). If other values are present they will be
11328 % 50%-thresholded to binary transparency. If more than 256
11329 % colors are present, they will be quantized to the 4-4-4-1,
11330 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11331 % of any resulting fully-transparent pixels is changed to
11332 % the image's background color.
11334 % If you want better quantization or dithering of the colors
11335 % or alpha than that, you need to do it before calling the
11336 % PNG encoder. The pixels contain 8-bit indices even if
11337 % they could be represented with 1, 2, or 4 bits. Grayscale
11338 % images will be written as indexed PNG files even though the
11339 % PNG grayscale type might be slightly more efficient. Please
11340 % note that writing to the PNG8 format may result in loss
11341 % of color and alpha data.
11343 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11344 % chunk can be present to convey binary transparency by naming
11345 % one of the colors as transparent. The only loss incurred
11346 % is reduction of sample depth to 8. If the image has more
11347 % than one transparent color, has semitransparent pixels, or
11348 % has an opaque pixel with the same RGB components as the
11349 % transparent color, an image is not written.
11351 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11352 % transparency is permitted, i.e., the alpha sample for
11353 % each pixel can have any value from 0 to 255. The alpha
11354 % channel is present even if the image is fully opaque.
11355 % The only loss in data is the reduction of the sample depth
11358 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11359 % chunk can be present to convey binary transparency by naming
11360 % one of the colors as transparent. If the image has more
11361 % than one transparent color, has semitransparent pixels, or
11362 % has an opaque pixel with the same RGB components as the
11363 % transparent color, an image is not written.
11365 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11366 % transparency is permitted, i.e., the alpha sample for
11367 % each pixel can have any value from 0 to 65535. The alpha
11368 % channel is present even if the image is fully opaque.
11370 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11371 % image, if the input was a PNG, is written. If these values
11372 % cannot be found, then "PNG00" falls back to the regular "PNG"
11375 % o -define: For more precise control of the PNG output, you can use the
11376 % Image options "png:bit-depth" and "png:color-type". These
11377 % can be set from the commandline with "-define" and also
11378 % from the application programming interfaces. The options
11379 % are case-independent and are converted to lowercase before
11380 % being passed to this encoder.
11382 % png:color-type can be 0, 2, 3, 4, or 6.
11384 % When png:color-type is 0 (Grayscale), png:bit-depth can
11385 % be 1, 2, 4, 8, or 16.
11387 % When png:color-type is 2 (RGB), png:bit-depth can
11390 % When png:color-type is 3 (Indexed), png:bit-depth can
11391 % be 1, 2, 4, or 8. This refers to the number of bits
11392 % used to store the index. The color samples always have
11393 % bit-depth 8 in indexed PNG files.
11395 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11396 % png:bit-depth can be 8 or 16.
11398 % If the image cannot be written without loss with the
11399 % requested bit-depth and color-type, a PNG file will not
11400 % be written, a warning will be issued, and the encoder will
11401 % return MagickFalse.
11403 % Since image encoders should not be responsible for the "heavy lifting",
11404 % the user should make sure that ImageMagick has already reduced the
11405 % image depth and number of colors and limit transparency to binary
11406 % transparency prior to attempting to write the image with depth, color,
11407 % or transparency limitations.
11409 % Note that another definition, "png:bit-depth-written" exists, but it
11410 % is not intended for external use. It is only used internally by the
11411 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11413 % It is possible to request that the PNG encoder write previously-formatted
11414 % ancillary chunks in the output PNG file, using the "-profile" commandline
11415 % option as shown below or by setting the profile via a programming
11418 % -profile PNG-chunk-x:<file>
11420 % where x is a location flag and <file> is a file containing the chunk
11421 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11422 % This encoder will compute the chunk length and CRC, so those must not
11423 % be included in the file.
11425 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11426 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11427 % of the same type, then add a short unique string after the "x" to prevent
11428 % subsequent profiles from overwriting the preceding ones, e.g.,
11430 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11432 % As of version 6.6.6 the following optimizations are always done:
11434 % o 32-bit depth is reduced to 16.
11435 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11436 % high byte and low byte are identical.
11437 % o Palette is sorted to remove unused entries and to put a
11438 % transparent color first, if BUILD_PNG_PALETTE is defined.
11439 % o Opaque matte channel is removed when writing an indexed PNG.
11440 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11441 % this can be done without loss and a larger bit depth N was not
11442 % requested via the "-define png:bit-depth=N" option.
11443 % o If matte channel is present but only one transparent color is
11444 % present, RGB+tRNS is written instead of RGBA
11445 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11446 % was requested when converting an opaque image).
11448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11450 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11451 Image *image,ExceptionInfo *exception)
11456 have_mng_structure,
11471 assert(image_info != (const ImageInfo *) NULL);
11472 assert(image_info->signature == MagickSignature);
11473 assert(image != (Image *) NULL);
11474 assert(image->signature == MagickSignature);
11475 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11476 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11478 Allocate a MngInfo structure.
11480 have_mng_structure=MagickFalse;
11481 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11483 if (mng_info == (MngInfo *) NULL)
11484 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11487 Initialize members of the MngInfo structure.
11489 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11490 mng_info->image=image;
11491 mng_info->equal_backgrounds=MagickTrue;
11492 have_mng_structure=MagickTrue;
11494 /* See if user has requested a specific PNG subformat */
11496 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11497 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11498 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11499 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11500 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11502 value=GetImageOption(image_info,"png:format");
11503 if (value == (char *) NULL)
11504 if (LocaleCompare(image_info->magick,"PNG00") == 0)
11506 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11509 " Format=%s",value);
11511 mng_info->write_png8 = MagickFalse;
11512 mng_info->write_png24 = MagickFalse;
11513 mng_info->write_png32 = MagickFalse;
11514 mng_info->write_png48 = MagickFalse;
11515 mng_info->write_png64 = MagickFalse;
11517 if (LocaleCompare(value,"png8") == 0)
11518 mng_info->write_png8 = MagickTrue;
11520 else if (LocaleCompare(value,"png24") == 0)
11521 mng_info->write_png24 = MagickTrue;
11523 else if (LocaleCompare(value,"png32") == 0)
11524 mng_info->write_png32 = MagickTrue;
11526 else if (LocaleCompare(value,"png48") == 0)
11527 mng_info->write_png48 = MagickTrue;
11529 else if (LocaleCompare(value,"png64") == 0)
11530 mng_info->write_png64 = MagickTrue;
11532 else if ((LocaleCompare(value,"png00") == 0) ||
11533 LocaleCompare(image_info->magick,"PNG00") == 0)
11535 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11536 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11538 if (value != (char *) NULL)
11540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11541 " png00 inherited bit depth=%s",value);
11543 if (LocaleCompare(value,"1") == 0)
11544 mng_info->write_png_depth = 1;
11546 else if (LocaleCompare(value,"2") == 0)
11547 mng_info->write_png_depth = 2;
11549 else if (LocaleCompare(value,"4") == 0)
11550 mng_info->write_png_depth = 4;
11552 else if (LocaleCompare(value,"8") == 0)
11553 mng_info->write_png_depth = 8;
11555 else if (LocaleCompare(value,"16") == 0)
11556 mng_info->write_png_depth = 16;
11559 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11561 if (value != (char *) NULL)
11563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11564 " png00 inherited color type=%s",value);
11566 if (LocaleCompare(value,"0") == 0)
11567 mng_info->write_png_colortype = 1;
11569 else if (LocaleCompare(value,"2") == 0)
11570 mng_info->write_png_colortype = 3;
11572 else if (LocaleCompare(value,"3") == 0)
11573 mng_info->write_png_colortype = 4;
11575 else if (LocaleCompare(value,"4") == 0)
11576 mng_info->write_png_colortype = 5;
11578 else if (LocaleCompare(value,"6") == 0)
11579 mng_info->write_png_colortype = 7;
11584 if (mng_info->write_png8)
11586 mng_info->write_png_colortype = /* 3 */ 4;
11587 mng_info->write_png_depth = 8;
11591 if (mng_info->write_png24)
11593 mng_info->write_png_colortype = /* 2 */ 3;
11594 mng_info->write_png_depth = 8;
11597 if (image->alpha_trait != UndefinedPixelTrait)
11598 (void) SetImageType(image,TrueColorAlphaType,exception);
11601 (void) SetImageType(image,TrueColorType,exception);
11603 (void) SyncImage(image,exception);
11606 if (mng_info->write_png32)
11608 mng_info->write_png_colortype = /* 6 */ 7;
11609 mng_info->write_png_depth = 8;
11611 image->alpha_trait = BlendPixelTrait;
11613 (void) SetImageType(image,TrueColorAlphaType,exception);
11614 (void) SyncImage(image,exception);
11617 if (mng_info->write_png48)
11619 mng_info->write_png_colortype = /* 2 */ 3;
11620 mng_info->write_png_depth = 16;
11623 if (image->alpha_trait != UndefinedPixelTrait)
11624 (void) SetImageType(image,TrueColorAlphaType,exception);
11627 (void) SetImageType(image,TrueColorType,exception);
11629 (void) SyncImage(image,exception);
11632 if (mng_info->write_png64)
11634 mng_info->write_png_colortype = /* 6 */ 7;
11635 mng_info->write_png_depth = 16;
11637 image->alpha_trait = BlendPixelTrait;
11639 (void) SetImageType(image,TrueColorAlphaType,exception);
11640 (void) SyncImage(image,exception);
11643 value=GetImageOption(image_info,"png:bit-depth");
11645 if (value != (char *) NULL)
11647 if (LocaleCompare(value,"1") == 0)
11648 mng_info->write_png_depth = 1;
11650 else if (LocaleCompare(value,"2") == 0)
11651 mng_info->write_png_depth = 2;
11653 else if (LocaleCompare(value,"4") == 0)
11654 mng_info->write_png_depth = 4;
11656 else if (LocaleCompare(value,"8") == 0)
11657 mng_info->write_png_depth = 8;
11659 else if (LocaleCompare(value,"16") == 0)
11660 mng_info->write_png_depth = 16;
11663 (void) ThrowMagickException(exception,
11664 GetMagickModule(),CoderWarning,
11665 "ignoring invalid defined png:bit-depth",
11668 if (logging != MagickFalse)
11669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11670 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11673 value=GetImageOption(image_info,"png:color-type");
11675 if (value != (char *) NULL)
11677 /* We must store colortype+1 because 0 is a valid colortype */
11678 if (LocaleCompare(value,"0") == 0)
11679 mng_info->write_png_colortype = 1;
11681 else if (LocaleCompare(value,"1") == 0)
11682 mng_info->write_png_colortype = 2;
11684 else if (LocaleCompare(value,"2") == 0)
11685 mng_info->write_png_colortype = 3;
11687 else if (LocaleCompare(value,"3") == 0)
11688 mng_info->write_png_colortype = 4;
11690 else if (LocaleCompare(value,"4") == 0)
11691 mng_info->write_png_colortype = 5;
11693 else if (LocaleCompare(value,"6") == 0)
11694 mng_info->write_png_colortype = 7;
11697 (void) ThrowMagickException(exception,
11698 GetMagickModule(),CoderWarning,
11699 "ignoring invalid defined png:color-type",
11702 if (logging != MagickFalse)
11703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11704 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11707 /* Check for chunks to be excluded:
11709 * The default is to not exclude any known chunks except for any
11710 * listed in the "unused_chunks" array, above.
11712 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11713 * define (in the image properties or in the image artifacts)
11714 * or via a mng_info member. For convenience, in addition
11715 * to or instead of a comma-separated list of chunks, the
11716 * "exclude-chunk" string can be simply "all" or "none".
11718 * The exclude-chunk define takes priority over the mng_info.
11720 * A "png:include-chunk" define takes priority over both the
11721 * mng_info and the "png:exclude-chunk" define. Like the
11722 * "exclude-chunk" string, it can define "all" or "none" as
11723 * well as a comma-separated list. Chunks that are unknown to
11724 * ImageMagick are always excluded, regardless of their "copy-safe"
11725 * status according to the PNG specification, and even if they
11726 * appear in the "include-chunk" list. Such defines appearing among
11727 * the image options take priority over those found among the image
11730 * Finally, all chunks listed in the "unused_chunks" array are
11731 * automatically excluded, regardless of the other instructions
11734 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11735 * will not be written and the gAMA chunk will only be written if it
11736 * is not between .45 and .46, or approximately (1.0/2.2).
11738 * If you exclude tRNS and the image has transparency, the colortype
11739 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11741 * The -strip option causes StripImage() to set the png:include-chunk
11742 * artifact to "none,trns,gama".
11745 mng_info->ping_exclude_bKGD=MagickFalse;
11746 mng_info->ping_exclude_cHRM=MagickFalse;
11747 mng_info->ping_exclude_date=MagickFalse;
11748 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11749 mng_info->ping_exclude_gAMA=MagickFalse;
11750 mng_info->ping_exclude_iCCP=MagickFalse;
11751 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11752 mng_info->ping_exclude_oFFs=MagickFalse;
11753 mng_info->ping_exclude_pHYs=MagickFalse;
11754 mng_info->ping_exclude_sRGB=MagickFalse;
11755 mng_info->ping_exclude_tEXt=MagickFalse;
11756 mng_info->ping_exclude_tIME=MagickFalse;
11757 mng_info->ping_exclude_tRNS=MagickFalse;
11758 mng_info->ping_exclude_vpAg=MagickFalse;
11759 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11760 mng_info->ping_exclude_zTXt=MagickFalse;
11762 mng_info->ping_preserve_colormap=MagickFalse;
11764 value=GetImageOption(image_info,"png:preserve-colormap");
11766 value=GetImageArtifact(image,"png:preserve-colormap");
11768 mng_info->ping_preserve_colormap=MagickTrue;
11770 mng_info->ping_preserve_iCCP=MagickFalse;
11772 value=GetImageOption(image_info,"png:preserve-iCCP");
11774 value=GetImageArtifact(image,"png:preserve-iCCP");
11776 mng_info->ping_preserve_iCCP=MagickTrue;
11778 /* These compression-level, compression-strategy, and compression-filter
11779 * defines take precedence over values from the -quality option.
11781 value=GetImageOption(image_info,"png:compression-level");
11783 value=GetImageArtifact(image,"png:compression-level");
11786 /* We have to add 1 to everything because 0 is a valid input,
11787 * and we want to use 0 (the default) to mean undefined.
11789 if (LocaleCompare(value,"0") == 0)
11790 mng_info->write_png_compression_level = 1;
11792 else if (LocaleCompare(value,"1") == 0)
11793 mng_info->write_png_compression_level = 2;
11795 else if (LocaleCompare(value,"2") == 0)
11796 mng_info->write_png_compression_level = 3;
11798 else if (LocaleCompare(value,"3") == 0)
11799 mng_info->write_png_compression_level = 4;
11801 else if (LocaleCompare(value,"4") == 0)
11802 mng_info->write_png_compression_level = 5;
11804 else if (LocaleCompare(value,"5") == 0)
11805 mng_info->write_png_compression_level = 6;
11807 else if (LocaleCompare(value,"6") == 0)
11808 mng_info->write_png_compression_level = 7;
11810 else if (LocaleCompare(value,"7") == 0)
11811 mng_info->write_png_compression_level = 8;
11813 else if (LocaleCompare(value,"8") == 0)
11814 mng_info->write_png_compression_level = 9;
11816 else if (LocaleCompare(value,"9") == 0)
11817 mng_info->write_png_compression_level = 10;
11820 (void) ThrowMagickException(exception,
11821 GetMagickModule(),CoderWarning,
11822 "ignoring invalid defined png:compression-level",
11826 value=GetImageOption(image_info,"png:compression-strategy");
11828 value=GetImageArtifact(image,"png:compression-strategy");
11831 if (LocaleCompare(value,"0") == 0)
11832 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11834 else if (LocaleCompare(value,"1") == 0)
11835 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11837 else if (LocaleCompare(value,"2") == 0)
11838 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11840 else if (LocaleCompare(value,"3") == 0)
11841 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11842 mng_info->write_png_compression_strategy = Z_RLE+1;
11844 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11847 else if (LocaleCompare(value,"4") == 0)
11848 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11849 mng_info->write_png_compression_strategy = Z_FIXED+1;
11851 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11855 (void) ThrowMagickException(exception,
11856 GetMagickModule(),CoderWarning,
11857 "ignoring invalid defined png:compression-strategy",
11861 value=GetImageOption(image_info,"png:compression-filter");
11863 value=GetImageArtifact(image,"png:compression-filter");
11866 /* To do: combinations of filters allowed by libpng
11867 * masks 0x08 through 0xf8
11869 * Implement this as a comma-separated list of 0,1,2,3,4,5
11870 * where 5 is a special case meaning PNG_ALL_FILTERS.
11873 if (LocaleCompare(value,"0") == 0)
11874 mng_info->write_png_compression_filter = 1;
11876 else if (LocaleCompare(value,"1") == 0)
11877 mng_info->write_png_compression_filter = 2;
11879 else if (LocaleCompare(value,"2") == 0)
11880 mng_info->write_png_compression_filter = 3;
11882 else if (LocaleCompare(value,"3") == 0)
11883 mng_info->write_png_compression_filter = 4;
11885 else if (LocaleCompare(value,"4") == 0)
11886 mng_info->write_png_compression_filter = 5;
11888 else if (LocaleCompare(value,"5") == 0)
11889 mng_info->write_png_compression_filter = 6;
11892 (void) ThrowMagickException(exception,
11893 GetMagickModule(),CoderWarning,
11894 "ignoring invalid defined png:compression-filter",
11898 for (source=0; source<8; source++)
11903 value=GetImageOption(image_info,"png:exclude-chunks");
11906 value=GetImageArtifact(image,"png:exclude-chunks");
11909 value=GetImageOption(image_info,"png:exclude-chunk");
11912 value=GetImageArtifact(image,"png:exclude-chunk");
11915 value=GetImageOption(image_info,"png:include-chunks");
11918 value=GetImageArtifact(image,"png:include-chunks");
11921 value=GetImageOption(image_info,"png:include-chunk");
11924 value=GetImageArtifact(image,"png:include-chunk");
11930 excluding = MagickTrue;
11932 excluding = MagickFalse;
11934 if (logging != MagickFalse)
11936 if (source == 0 || source == 2)
11937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11938 " png:exclude-chunk=%s found in image options.\n", value);
11939 else if (source == 1 || source == 3)
11940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11941 " png:exclude-chunk=%s found in image artifacts.\n", value);
11942 else if (source == 4 || source == 6)
11943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11944 " png:include-chunk=%s found in image options.\n", value);
11945 else /* if (source == 5 || source == 7) */
11946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11947 " png:include-chunk=%s found in image artifacts.\n", value);
11950 if (IsOptionMember("all",value) != MagickFalse)
11952 mng_info->ping_exclude_bKGD=excluding;
11953 mng_info->ping_exclude_cHRM=excluding;
11954 mng_info->ping_exclude_date=excluding;
11955 mng_info->ping_exclude_EXIF=excluding;
11956 mng_info->ping_exclude_gAMA=excluding;
11957 mng_info->ping_exclude_iCCP=excluding;
11958 /* mng_info->ping_exclude_iTXt=excluding; */
11959 mng_info->ping_exclude_oFFs=excluding;
11960 mng_info->ping_exclude_pHYs=excluding;
11961 mng_info->ping_exclude_sRGB=excluding;
11962 mng_info->ping_exclude_tEXt=excluding;
11963 mng_info->ping_exclude_tIME=excluding;
11964 mng_info->ping_exclude_tRNS=excluding;
11965 mng_info->ping_exclude_vpAg=excluding;
11966 mng_info->ping_exclude_zCCP=excluding;
11967 mng_info->ping_exclude_zTXt=excluding;
11970 if (IsOptionMember("none",value) != MagickFalse)
11972 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
11974 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
11976 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
11978 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
11980 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
11982 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
11984 /* mng_info->ping_exclude_iTXt=!excluding; */
11985 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
11987 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
11989 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
11991 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
11993 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
11995 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
11997 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
11999 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12001 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12005 if (IsOptionMember("bkgd",value) != MagickFalse)
12006 mng_info->ping_exclude_bKGD=excluding;
12008 if (IsOptionMember("chrm",value) != MagickFalse)
12009 mng_info->ping_exclude_cHRM=excluding;
12011 if (IsOptionMember("date",value) != MagickFalse)
12012 mng_info->ping_exclude_date=excluding;
12014 if (IsOptionMember("exif",value) != MagickFalse)
12015 mng_info->ping_exclude_EXIF=excluding;
12017 if (IsOptionMember("gama",value) != MagickFalse)
12018 mng_info->ping_exclude_gAMA=excluding;
12020 if (IsOptionMember("iccp",value) != MagickFalse)
12021 mng_info->ping_exclude_iCCP=excluding;
12024 if (IsOptionMember("itxt",value) != MagickFalse)
12025 mng_info->ping_exclude_iTXt=excluding;
12028 if (IsOptionMember("offs",value) != MagickFalse)
12029 mng_info->ping_exclude_oFFs=excluding;
12031 if (IsOptionMember("phys",value) != MagickFalse)
12032 mng_info->ping_exclude_pHYs=excluding;
12034 if (IsOptionMember("srgb",value) != MagickFalse)
12035 mng_info->ping_exclude_sRGB=excluding;
12037 if (IsOptionMember("text",value) != MagickFalse)
12038 mng_info->ping_exclude_tEXt=excluding;
12040 if (IsOptionMember("time",value) != MagickFalse)
12041 mng_info->ping_exclude_tIME=excluding;
12043 if (IsOptionMember("trns",value) != MagickFalse)
12044 mng_info->ping_exclude_tRNS=excluding;
12046 if (IsOptionMember("vpag",value) != MagickFalse)
12047 mng_info->ping_exclude_vpAg=excluding;
12049 if (IsOptionMember("zccp",value) != MagickFalse)
12050 mng_info->ping_exclude_zCCP=excluding;
12052 if (IsOptionMember("ztxt",value) != MagickFalse)
12053 mng_info->ping_exclude_zTXt=excluding;
12056 if (logging != MagickFalse)
12058 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12059 " Chunks to be excluded from the output png:");
12060 if (mng_info->ping_exclude_bKGD != MagickFalse)
12061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12063 if (mng_info->ping_exclude_cHRM != MagickFalse)
12064 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12066 if (mng_info->ping_exclude_date != MagickFalse)
12067 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12069 if (mng_info->ping_exclude_EXIF != MagickFalse)
12070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12072 if (mng_info->ping_exclude_gAMA != MagickFalse)
12073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12075 if (mng_info->ping_exclude_iCCP != MagickFalse)
12076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12079 if (mng_info->ping_exclude_iTXt != MagickFalse)
12080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12084 if (mng_info->ping_exclude_oFFs != MagickFalse)
12085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12087 if (mng_info->ping_exclude_pHYs != MagickFalse)
12088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12090 if (mng_info->ping_exclude_sRGB != MagickFalse)
12091 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12093 if (mng_info->ping_exclude_tEXt != MagickFalse)
12094 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12096 if (mng_info->ping_exclude_tIME != MagickFalse)
12097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12099 if (mng_info->ping_exclude_tRNS != MagickFalse)
12100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12102 if (mng_info->ping_exclude_vpAg != MagickFalse)
12103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12105 if (mng_info->ping_exclude_zCCP != MagickFalse)
12106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12108 if (mng_info->ping_exclude_zTXt != MagickFalse)
12109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12113 mng_info->need_blob = MagickTrue;
12115 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12117 MngInfoFreeStruct(mng_info,&have_mng_structure);
12119 if (logging != MagickFalse)
12120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12125 #if defined(JNG_SUPPORTED)
12127 /* Write one JNG image */
12128 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12129 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12150 jng_alpha_compression_method,
12151 jng_alpha_sample_depth,
12159 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12160 " Enter WriteOneJNGImage()");
12162 blob=(unsigned char *) NULL;
12163 jpeg_image=(Image *) NULL;
12164 jpeg_image_info=(ImageInfo *) NULL;
12168 transparent=image_info->type==GrayscaleAlphaType ||
12169 image_info->type==TrueColorAlphaType ||
12170 image->alpha_trait != UndefinedPixelTrait;
12172 jng_alpha_sample_depth = 0;
12174 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12176 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12178 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12179 image_info->quality;
12181 if (jng_alpha_quality >= 1000)
12182 jng_alpha_quality /= 1000;
12186 if (transparent != 0)
12190 /* Create JPEG blob, image, and image_info */
12191 if (logging != MagickFalse)
12192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12193 " Creating jpeg_image_info for alpha.");
12195 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12197 if (jpeg_image_info == (ImageInfo *) NULL)
12198 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12200 if (logging != MagickFalse)
12201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12202 " Creating jpeg_image.");
12204 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12205 if (jpeg_image == (Image *) NULL)
12206 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12207 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12208 jpeg_image->alpha_trait=UndefinedPixelTrait;
12209 jpeg_image->quality=jng_alpha_quality;
12210 jpeg_image_info->type=GrayscaleType;
12211 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12212 (void) AcquireUniqueFilename(jpeg_image->filename);
12213 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12214 "%s",jpeg_image->filename);
12218 jng_alpha_compression_method=0;
12220 jng_alpha_sample_depth=0;
12223 /* To do: check bit depth of PNG alpha channel */
12225 /* Check if image is grayscale. */
12226 if (image_info->type != TrueColorAlphaType && image_info->type !=
12227 TrueColorType && SetImageGray(image,exception))
12230 if (logging != MagickFalse)
12232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12233 " JNG Quality = %d",(int) jng_quality);
12234 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12235 " JNG Color Type = %d",jng_color_type);
12236 if (transparent != 0)
12238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12239 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12241 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12243 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12247 if (transparent != 0)
12249 if (jng_alpha_compression_method==0)
12254 /* Encode alpha as a grayscale PNG blob */
12255 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12257 if (status == MagickFalse)
12258 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12260 if (logging != MagickFalse)
12261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12262 " Creating PNG blob.");
12264 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12265 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12266 jpeg_image_info->interlace=NoInterlace;
12268 /* Exclude all ancillary chunks */
12269 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12271 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12274 /* Retrieve sample depth used */
12275 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12276 if (value != (char *) NULL)
12277 jng_alpha_sample_depth= (unsigned int) value[0];
12281 /* Encode alpha as a grayscale JPEG blob */
12283 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12285 if (status == MagickFalse)
12286 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12289 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12290 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12291 jpeg_image_info->interlace=NoInterlace;
12292 if (logging != MagickFalse)
12293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12294 " Creating blob.");
12295 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12297 jng_alpha_sample_depth=8;
12299 if (logging != MagickFalse)
12300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12301 " Successfully read jpeg_image into a blob, length=%.20g.",
12305 /* Destroy JPEG image and image_info */
12306 jpeg_image=DestroyImage(jpeg_image);
12307 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12308 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12311 /* Write JHDR chunk */
12312 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12313 PNGType(chunk,mng_JHDR);
12314 LogPNGChunk(logging,mng_JHDR,16L);
12315 PNGLong(chunk+4,(png_uint_32) image->columns);
12316 PNGLong(chunk+8,(png_uint_32) image->rows);
12317 chunk[12]=jng_color_type;
12318 chunk[13]=8; /* sample depth */
12319 chunk[14]=8; /*jng_image_compression_method */
12320 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12321 chunk[16]=jng_alpha_sample_depth;
12322 chunk[17]=jng_alpha_compression_method;
12323 chunk[18]=0; /*jng_alpha_filter_method */
12324 chunk[19]=0; /*jng_alpha_interlace_method */
12325 (void) WriteBlob(image,20,chunk);
12326 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12327 if (logging != MagickFalse)
12329 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12330 " JNG width:%15lu",(unsigned long) image->columns);
12332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12333 " JNG height:%14lu",(unsigned long) image->rows);
12335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12336 " JNG color type:%10d",jng_color_type);
12338 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12339 " JNG sample depth:%8d",8);
12341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12342 " JNG compression:%9d",8);
12344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12345 " JNG interlace:%11d",0);
12347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12348 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12351 " JNG alpha compression:%3d",jng_alpha_compression_method);
12353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12354 " JNG alpha filter:%8d",0);
12356 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12357 " JNG alpha interlace:%5d",0);
12360 /* Write any JNG-chunk-b profiles */
12361 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12364 Write leading ancillary chunks
12367 if (transparent != 0)
12370 Write JNG bKGD chunk
12381 if (jng_color_type == 8 || jng_color_type == 12)
12385 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12386 PNGType(chunk,mng_bKGD);
12387 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12388 red=ScaleQuantumToChar(image->background_color.red);
12389 green=ScaleQuantumToChar(image->background_color.green);
12390 blue=ScaleQuantumToChar(image->background_color.blue);
12397 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12398 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12401 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12404 Write JNG sRGB chunk
12406 (void) WriteBlobMSBULong(image,1L);
12407 PNGType(chunk,mng_sRGB);
12408 LogPNGChunk(logging,mng_sRGB,1L);
12410 if (image->rendering_intent != UndefinedIntent)
12411 chunk[4]=(unsigned char)
12412 Magick_RenderingIntent_to_PNG_RenderingIntent(
12413 (image->rendering_intent));
12416 chunk[4]=(unsigned char)
12417 Magick_RenderingIntent_to_PNG_RenderingIntent(
12418 (PerceptualIntent));
12420 (void) WriteBlob(image,5,chunk);
12421 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12425 if (image->gamma != 0.0)
12428 Write JNG gAMA chunk
12430 (void) WriteBlobMSBULong(image,4L);
12431 PNGType(chunk,mng_gAMA);
12432 LogPNGChunk(logging,mng_gAMA,4L);
12433 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12434 (void) WriteBlob(image,8,chunk);
12435 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12438 if ((mng_info->equal_chrms == MagickFalse) &&
12439 (image->chromaticity.red_primary.x != 0.0))
12445 Write JNG cHRM chunk
12447 (void) WriteBlobMSBULong(image,32L);
12448 PNGType(chunk,mng_cHRM);
12449 LogPNGChunk(logging,mng_cHRM,32L);
12450 primary=image->chromaticity.white_point;
12451 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12452 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12453 primary=image->chromaticity.red_primary;
12454 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12455 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12456 primary=image->chromaticity.green_primary;
12457 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12458 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12459 primary=image->chromaticity.blue_primary;
12460 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12461 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12462 (void) WriteBlob(image,36,chunk);
12463 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12467 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12470 Write JNG pHYs chunk
12472 (void) WriteBlobMSBULong(image,9L);
12473 PNGType(chunk,mng_pHYs);
12474 LogPNGChunk(logging,mng_pHYs,9L);
12475 if (image->units == PixelsPerInchResolution)
12477 PNGLong(chunk+4,(png_uint_32)
12478 (image->resolution.x*100.0/2.54+0.5));
12480 PNGLong(chunk+8,(png_uint_32)
12481 (image->resolution.y*100.0/2.54+0.5));
12488 if (image->units == PixelsPerCentimeterResolution)
12490 PNGLong(chunk+4,(png_uint_32)
12491 (image->resolution.x*100.0+0.5));
12493 PNGLong(chunk+8,(png_uint_32)
12494 (image->resolution.y*100.0+0.5));
12501 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12502 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12506 (void) WriteBlob(image,13,chunk);
12507 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12510 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12513 Write JNG oFFs chunk
12515 (void) WriteBlobMSBULong(image,9L);
12516 PNGType(chunk,mng_oFFs);
12517 LogPNGChunk(logging,mng_oFFs,9L);
12518 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12519 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12521 (void) WriteBlob(image,13,chunk);
12522 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12524 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12526 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12527 PNGType(chunk,mng_vpAg);
12528 LogPNGChunk(logging,mng_vpAg,9L);
12529 PNGLong(chunk+4,(png_uint_32) image->page.width);
12530 PNGLong(chunk+8,(png_uint_32) image->page.height);
12531 chunk[12]=0; /* unit = pixels */
12532 (void) WriteBlob(image,13,chunk);
12533 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12536 if (transparent != 0)
12538 if (jng_alpha_compression_method==0)
12546 /* Write IDAT chunk header */
12547 if (logging != MagickFalse)
12548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12549 " Write IDAT chunks from blob, length=%.20g.",(double)
12552 /* Copy IDAT chunks */
12555 for (i=8; i<(ssize_t) length; i+=len+12)
12557 len=(size_t) (*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12560 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12562 /* Found an IDAT chunk. */
12563 (void) WriteBlobMSBULong(image,len);
12564 LogPNGChunk(logging,mng_IDAT,len);
12565 (void) WriteBlob(image,len+4,p);
12566 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12571 if (logging != MagickFalse)
12572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12573 " Skipping %c%c%c%c chunk, length=%.20g.",
12574 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12579 else if (length != 0)
12581 /* Write JDAA chunk header */
12582 if (logging != MagickFalse)
12583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12584 " Write JDAA chunk, length=%.20g.",(double) length);
12585 (void) WriteBlobMSBULong(image,(size_t) length);
12586 PNGType(chunk,mng_JDAA);
12587 LogPNGChunk(logging,mng_JDAA,length);
12588 /* Write JDAT chunk(s) data */
12589 (void) WriteBlob(image,4,chunk);
12590 (void) WriteBlob(image,length,blob);
12591 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12594 blob=(unsigned char *) RelinquishMagickMemory(blob);
12597 /* Encode image as a JPEG blob */
12598 if (logging != MagickFalse)
12599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12600 " Creating jpeg_image_info.");
12601 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12602 if (jpeg_image_info == (ImageInfo *) NULL)
12603 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12605 if (logging != MagickFalse)
12606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12607 " Creating jpeg_image.");
12609 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12610 if (jpeg_image == (Image *) NULL)
12611 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12612 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12614 (void) AcquireUniqueFilename(jpeg_image->filename);
12615 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12616 jpeg_image->filename);
12618 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12621 if (logging != MagickFalse)
12622 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12623 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12624 (double) jpeg_image->rows);
12626 if (status == MagickFalse)
12627 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12629 if (jng_color_type == 8 || jng_color_type == 12)
12630 jpeg_image_info->type=GrayscaleType;
12632 jpeg_image_info->quality=jng_quality;
12633 jpeg_image->quality=jng_quality;
12634 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12635 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12637 if (logging != MagickFalse)
12638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12639 " Creating blob.");
12641 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,exception);
12643 if (logging != MagickFalse)
12645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12646 " Successfully read jpeg_image into a blob, length=%.20g.",
12649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12650 " Write JDAT chunk, length=%.20g.",(double) length);
12653 /* Write JDAT chunk(s) */
12654 (void) WriteBlobMSBULong(image,(size_t) length);
12655 PNGType(chunk,mng_JDAT);
12656 LogPNGChunk(logging,mng_JDAT,length);
12657 (void) WriteBlob(image,4,chunk);
12658 (void) WriteBlob(image,length,blob);
12659 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12661 jpeg_image=DestroyImage(jpeg_image);
12662 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12663 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12664 blob=(unsigned char *) RelinquishMagickMemory(blob);
12666 /* Write any JNG-chunk-e profiles */
12667 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12669 /* Write IEND chunk */
12670 (void) WriteBlobMSBULong(image,0L);
12671 PNGType(chunk,mng_IEND);
12672 LogPNGChunk(logging,mng_IEND,0);
12673 (void) WriteBlob(image,4,chunk);
12674 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12676 if (logging != MagickFalse)
12677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12678 " exit WriteOneJNGImage()");
12685 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12689 % W r i t e J N G I m a g e %
12693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12695 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12697 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12699 % The format of the WriteJNGImage method is:
12701 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12702 % Image *image,ExceptionInfo *exception)
12704 % A description of each parameter follows:
12706 % o image_info: the image info.
12708 % o image: The image.
12710 % o exception: return any errors or warnings in this structure.
12712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12714 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12715 ExceptionInfo *exception)
12718 have_mng_structure,
12728 assert(image_info != (const ImageInfo *) NULL);
12729 assert(image_info->signature == MagickSignature);
12730 assert(image != (Image *) NULL);
12731 assert(image->signature == MagickSignature);
12732 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12733 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12734 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12735 if (status == MagickFalse)
12739 Allocate a MngInfo structure.
12741 have_mng_structure=MagickFalse;
12742 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12743 if (mng_info == (MngInfo *) NULL)
12744 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12746 Initialize members of the MngInfo structure.
12748 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12749 mng_info->image=image;
12750 have_mng_structure=MagickTrue;
12752 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12754 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12755 (void) CloseBlob(image);
12757 (void) CatchImageException(image);
12758 MngInfoFreeStruct(mng_info,&have_mng_structure);
12759 if (logging != MagickFalse)
12760 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12765 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12766 ExceptionInfo *exception)
12775 have_mng_structure,
12778 volatile MagickBooleanType
12790 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12791 defined(PNG_MNG_FEATURES_SUPPORTED)
12794 all_images_are_gray,
12804 volatile unsigned int
12815 #if (PNG_LIBPNG_VER < 10200)
12816 if (image_info->verbose)
12817 printf("Your PNG library (libpng-%s) is rather old.\n",
12818 PNG_LIBPNG_VER_STRING);
12824 assert(image_info != (const ImageInfo *) NULL);
12825 assert(image_info->signature == MagickSignature);
12826 assert(image != (Image *) NULL);
12827 assert(image->signature == MagickSignature);
12828 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12829 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12830 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12831 if (status == MagickFalse)
12835 Allocate a MngInfo structure.
12837 have_mng_structure=MagickFalse;
12838 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12839 if (mng_info == (MngInfo *) NULL)
12840 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12842 Initialize members of the MngInfo structure.
12844 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12845 mng_info->image=image;
12846 have_mng_structure=MagickTrue;
12847 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12850 * See if user has requested a specific PNG subformat to be used
12851 * for all of the PNGs in the MNG being written, e.g.,
12853 * convert *.png png8:animation.mng
12855 * To do: check -define png:bit_depth and png:color_type as well,
12856 * or perhaps use mng:bit_depth and mng:color_type instead for
12860 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12861 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12862 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12864 write_jng=MagickFalse;
12865 if (image_info->compression == JPEGCompression)
12866 write_jng=MagickTrue;
12868 mng_info->adjoin=image_info->adjoin &&
12869 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12871 if (logging != MagickFalse)
12873 /* Log some info about the input */
12877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12878 " Checking input image(s)\n"
12879 " Image_info depth: %.20g, Type: %d",
12880 (double) image_info->depth, image_info->type);
12883 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12886 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12887 " Scene: %.20g\n, Image depth: %.20g",
12888 (double) scene++, (double) p->depth);
12890 if (p->alpha_trait != UndefinedPixelTrait)
12891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12898 if (p->storage_class == PseudoClass)
12899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12900 " Storage class: PseudoClass");
12903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12904 " Storage class: DirectClass");
12907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12908 " Number of colors: %.20g",(double) p->colors);
12911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12912 " Number of colors: unspecified");
12914 if (mng_info->adjoin == MagickFalse)
12919 use_global_plte=MagickFalse;
12920 all_images_are_gray=MagickFalse;
12921 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12922 need_local_plte=MagickTrue;
12924 need_defi=MagickFalse;
12925 need_matte=MagickFalse;
12926 mng_info->framing_mode=1;
12927 mng_info->old_framing_mode=1;
12930 if (image_info->page != (char *) NULL)
12933 Determine image bounding box.
12935 SetGeometry(image,&mng_info->page);
12936 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
12937 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
12949 mng_info->page=image->page;
12950 need_geom=MagickTrue;
12951 if (mng_info->page.width || mng_info->page.height)
12952 need_geom=MagickFalse;
12954 Check all the scenes.
12956 initial_delay=image->delay;
12957 need_iterations=MagickFalse;
12958 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
12959 mng_info->equal_physs=MagickTrue,
12960 mng_info->equal_gammas=MagickTrue;
12961 mng_info->equal_srgbs=MagickTrue;
12962 mng_info->equal_backgrounds=MagickTrue;
12964 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12965 defined(PNG_MNG_FEATURES_SUPPORTED)
12966 all_images_are_gray=MagickTrue;
12967 mng_info->equal_palettes=MagickFalse;
12968 need_local_plte=MagickFalse;
12970 for (next_image=image; next_image != (Image *) NULL; )
12974 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
12975 mng_info->page.width=next_image->columns+next_image->page.x;
12977 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
12978 mng_info->page.height=next_image->rows+next_image->page.y;
12981 if (next_image->page.x || next_image->page.y)
12982 need_defi=MagickTrue;
12984 if (next_image->alpha_trait != UndefinedPixelTrait)
12985 need_matte=MagickTrue;
12987 if ((int) next_image->dispose >= BackgroundDispose)
12988 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
12989 next_image->page.x || next_image->page.y ||
12990 ((next_image->columns < mng_info->page.width) &&
12991 (next_image->rows < mng_info->page.height)))
12992 mng_info->need_fram=MagickTrue;
12994 if (next_image->iterations)
12995 need_iterations=MagickTrue;
12997 final_delay=next_image->delay;
12999 if (final_delay != initial_delay || final_delay > 1UL*
13000 next_image->ticks_per_second)
13001 mng_info->need_fram=1;
13003 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13004 defined(PNG_MNG_FEATURES_SUPPORTED)
13006 check for global palette possibility.
13008 if (image->alpha_trait != UndefinedPixelTrait)
13009 need_local_plte=MagickTrue;
13011 if (need_local_plte == 0)
13013 if (SetImageGray(image,exception) == MagickFalse)
13014 all_images_are_gray=MagickFalse;
13015 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13016 if (use_global_plte == 0)
13017 use_global_plte=mng_info->equal_palettes;
13018 need_local_plte=!mng_info->equal_palettes;
13021 if (GetNextImageInList(next_image) != (Image *) NULL)
13023 if (next_image->background_color.red !=
13024 next_image->next->background_color.red ||
13025 next_image->background_color.green !=
13026 next_image->next->background_color.green ||
13027 next_image->background_color.blue !=
13028 next_image->next->background_color.blue)
13029 mng_info->equal_backgrounds=MagickFalse;
13031 if (next_image->gamma != next_image->next->gamma)
13032 mng_info->equal_gammas=MagickFalse;
13034 if (next_image->rendering_intent !=
13035 next_image->next->rendering_intent)
13036 mng_info->equal_srgbs=MagickFalse;
13038 if ((next_image->units != next_image->next->units) ||
13039 (next_image->resolution.x != next_image->next->resolution.x) ||
13040 (next_image->resolution.y != next_image->next->resolution.y))
13041 mng_info->equal_physs=MagickFalse;
13043 if (mng_info->equal_chrms)
13045 if (next_image->chromaticity.red_primary.x !=
13046 next_image->next->chromaticity.red_primary.x ||
13047 next_image->chromaticity.red_primary.y !=
13048 next_image->next->chromaticity.red_primary.y ||
13049 next_image->chromaticity.green_primary.x !=
13050 next_image->next->chromaticity.green_primary.x ||
13051 next_image->chromaticity.green_primary.y !=
13052 next_image->next->chromaticity.green_primary.y ||
13053 next_image->chromaticity.blue_primary.x !=
13054 next_image->next->chromaticity.blue_primary.x ||
13055 next_image->chromaticity.blue_primary.y !=
13056 next_image->next->chromaticity.blue_primary.y ||
13057 next_image->chromaticity.white_point.x !=
13058 next_image->next->chromaticity.white_point.x ||
13059 next_image->chromaticity.white_point.y !=
13060 next_image->next->chromaticity.white_point.y)
13061 mng_info->equal_chrms=MagickFalse;
13065 next_image=GetNextImageInList(next_image);
13067 if (image_count < 2)
13069 mng_info->equal_backgrounds=MagickFalse;
13070 mng_info->equal_chrms=MagickFalse;
13071 mng_info->equal_gammas=MagickFalse;
13072 mng_info->equal_srgbs=MagickFalse;
13073 mng_info->equal_physs=MagickFalse;
13074 use_global_plte=MagickFalse;
13075 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13076 need_local_plte=MagickTrue;
13078 need_iterations=MagickFalse;
13081 if (mng_info->need_fram == MagickFalse)
13084 Only certain framing rates 100/n are exactly representable without
13085 the FRAM chunk but we'll allow some slop in VLC files
13087 if (final_delay == 0)
13089 if (need_iterations != MagickFalse)
13092 It's probably a GIF with loop; don't run it *too* fast.
13094 if (mng_info->adjoin)
13097 (void) ThrowMagickException(exception,GetMagickModule(),
13099 "input has zero delay between all frames; assuming",
13104 mng_info->ticks_per_second=0;
13106 if (final_delay != 0)
13107 mng_info->ticks_per_second=(png_uint_32)
13108 (image->ticks_per_second/final_delay);
13109 if (final_delay > 50)
13110 mng_info->ticks_per_second=2;
13112 if (final_delay > 75)
13113 mng_info->ticks_per_second=1;
13115 if (final_delay > 125)
13116 mng_info->need_fram=MagickTrue;
13118 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13119 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13120 (final_delay != 25) && (final_delay != 50) && (1UL*final_delay !=
13121 1UL*image->ticks_per_second))
13122 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13125 if (mng_info->need_fram != MagickFalse)
13126 mng_info->ticks_per_second=1UL*image->ticks_per_second;
13128 If pseudocolor, we should also check to see if all the
13129 palettes are identical and write a global PLTE if they are.
13133 Write the MNG version 1.0 signature and MHDR chunk.
13135 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13136 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13137 PNGType(chunk,mng_MHDR);
13138 LogPNGChunk(logging,mng_MHDR,28L);
13139 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13140 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13141 PNGLong(chunk+12,mng_info->ticks_per_second);
13142 PNGLong(chunk+16,0L); /* layer count=unknown */
13143 PNGLong(chunk+20,0L); /* frame count=unknown */
13144 PNGLong(chunk+24,0L); /* play time=unknown */
13149 if (need_defi || mng_info->need_fram || use_global_plte)
13150 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13153 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13158 if (need_defi || mng_info->need_fram || use_global_plte)
13159 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13162 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13170 if (need_defi || mng_info->need_fram || use_global_plte)
13171 PNGLong(chunk+28,11L); /* simplicity=LC */
13174 PNGLong(chunk+28,9L); /* simplicity=VLC */
13179 if (need_defi || mng_info->need_fram || use_global_plte)
13180 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13183 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13186 (void) WriteBlob(image,32,chunk);
13187 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13188 option=GetImageOption(image_info,"mng:need-cacheoff");
13189 if (option != (const char *) NULL)
13195 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13197 PNGType(chunk,mng_nEED);
13198 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13199 (void) WriteBlobMSBULong(image,(size_t) length);
13200 LogPNGChunk(logging,mng_nEED,(size_t) length);
13202 (void) WriteBlob(image,length,chunk);
13203 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13205 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13206 (GetNextImageInList(image) != (Image *) NULL) &&
13207 (image->iterations != 1))
13210 Write MNG TERM chunk
13212 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13213 PNGType(chunk,mng_TERM);
13214 LogPNGChunk(logging,mng_TERM,10L);
13215 chunk[4]=3; /* repeat animation */
13216 chunk[5]=0; /* show last frame when done */
13217 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13218 final_delay/MagickMax(image->ticks_per_second,1)));
13220 if (image->iterations == 0)
13221 PNGLong(chunk+10,PNG_UINT_31_MAX);
13224 PNGLong(chunk+10,(png_uint_32) image->iterations);
13226 if (logging != MagickFalse)
13228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13229 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13230 final_delay/MagickMax(image->ticks_per_second,1)));
13232 if (image->iterations == 0)
13233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13234 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13238 " Image iterations: %.20g",(double) image->iterations);
13240 (void) WriteBlob(image,14,chunk);
13241 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13244 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13246 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13247 mng_info->equal_srgbs)
13250 Write MNG sRGB chunk
13252 (void) WriteBlobMSBULong(image,1L);
13253 PNGType(chunk,mng_sRGB);
13254 LogPNGChunk(logging,mng_sRGB,1L);
13256 if (image->rendering_intent != UndefinedIntent)
13257 chunk[4]=(unsigned char)
13258 Magick_RenderingIntent_to_PNG_RenderingIntent(
13259 (image->rendering_intent));
13262 chunk[4]=(unsigned char)
13263 Magick_RenderingIntent_to_PNG_RenderingIntent(
13264 (PerceptualIntent));
13266 (void) WriteBlob(image,5,chunk);
13267 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13268 mng_info->have_write_global_srgb=MagickTrue;
13273 if (image->gamma && mng_info->equal_gammas)
13276 Write MNG gAMA chunk
13278 (void) WriteBlobMSBULong(image,4L);
13279 PNGType(chunk,mng_gAMA);
13280 LogPNGChunk(logging,mng_gAMA,4L);
13281 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13282 (void) WriteBlob(image,8,chunk);
13283 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13284 mng_info->have_write_global_gama=MagickTrue;
13286 if (mng_info->equal_chrms)
13292 Write MNG cHRM chunk
13294 (void) WriteBlobMSBULong(image,32L);
13295 PNGType(chunk,mng_cHRM);
13296 LogPNGChunk(logging,mng_cHRM,32L);
13297 primary=image->chromaticity.white_point;
13298 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13299 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13300 primary=image->chromaticity.red_primary;
13301 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13302 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13303 primary=image->chromaticity.green_primary;
13304 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13305 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13306 primary=image->chromaticity.blue_primary;
13307 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13308 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13309 (void) WriteBlob(image,36,chunk);
13310 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13311 mng_info->have_write_global_chrm=MagickTrue;
13314 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13317 Write MNG pHYs chunk
13319 (void) WriteBlobMSBULong(image,9L);
13320 PNGType(chunk,mng_pHYs);
13321 LogPNGChunk(logging,mng_pHYs,9L);
13323 if (image->units == PixelsPerInchResolution)
13325 PNGLong(chunk+4,(png_uint_32)
13326 (image->resolution.x*100.0/2.54+0.5));
13328 PNGLong(chunk+8,(png_uint_32)
13329 (image->resolution.y*100.0/2.54+0.5));
13336 if (image->units == PixelsPerCentimeterResolution)
13338 PNGLong(chunk+4,(png_uint_32)
13339 (image->resolution.x*100.0+0.5));
13341 PNGLong(chunk+8,(png_uint_32)
13342 (image->resolution.y*100.0+0.5));
13349 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13350 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13354 (void) WriteBlob(image,13,chunk);
13355 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13358 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13359 or does not cover the entire frame.
13361 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13362 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13363 (image->page.width+image->page.x < mng_info->page.width))
13364 || (image->page.height && (image->page.height+image->page.y
13365 < mng_info->page.height))))
13367 (void) WriteBlobMSBULong(image,6L);
13368 PNGType(chunk,mng_BACK);
13369 LogPNGChunk(logging,mng_BACK,6L);
13370 red=ScaleQuantumToShort(image->background_color.red);
13371 green=ScaleQuantumToShort(image->background_color.green);
13372 blue=ScaleQuantumToShort(image->background_color.blue);
13373 PNGShort(chunk+4,red);
13374 PNGShort(chunk+6,green);
13375 PNGShort(chunk+8,blue);
13376 (void) WriteBlob(image,10,chunk);
13377 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13378 if (mng_info->equal_backgrounds)
13380 (void) WriteBlobMSBULong(image,6L);
13381 PNGType(chunk,mng_bKGD);
13382 LogPNGChunk(logging,mng_bKGD,6L);
13383 (void) WriteBlob(image,10,chunk);
13384 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13388 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13389 if ((need_local_plte == MagickFalse) &&
13390 (image->storage_class == PseudoClass) &&
13391 (all_images_are_gray == MagickFalse))
13397 Write MNG PLTE chunk
13399 data_length=3*image->colors;
13400 (void) WriteBlobMSBULong(image,data_length);
13401 PNGType(chunk,mng_PLTE);
13402 LogPNGChunk(logging,mng_PLTE,data_length);
13404 for (i=0; i < (ssize_t) image->colors; i++)
13406 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13407 image->colormap[i].red) & 0xff);
13408 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13409 image->colormap[i].green) & 0xff);
13410 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13411 image->colormap[i].blue) & 0xff);
13414 (void) WriteBlob(image,data_length+4,chunk);
13415 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13416 mng_info->have_write_global_plte=MagickTrue;
13422 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13423 defined(PNG_MNG_FEATURES_SUPPORTED)
13424 mng_info->equal_palettes=MagickFalse;
13428 if (mng_info->adjoin)
13430 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13431 defined(PNG_MNG_FEATURES_SUPPORTED)
13433 If we aren't using a global palette for the entire MNG, check to
13434 see if we can use one for two or more consecutive images.
13436 if (need_local_plte && use_global_plte && !all_images_are_gray)
13438 if (mng_info->IsPalette)
13441 When equal_palettes is true, this image has the same palette
13442 as the previous PseudoClass image
13444 mng_info->have_write_global_plte=mng_info->equal_palettes;
13445 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13446 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13449 Write MNG PLTE chunk
13454 data_length=3*image->colors;
13455 (void) WriteBlobMSBULong(image,data_length);
13456 PNGType(chunk,mng_PLTE);
13457 LogPNGChunk(logging,mng_PLTE,data_length);
13459 for (i=0; i < (ssize_t) image->colors; i++)
13461 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13462 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13463 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13466 (void) WriteBlob(image,data_length+4,chunk);
13467 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13468 (uInt) (data_length+4)));
13469 mng_info->have_write_global_plte=MagickTrue;
13473 mng_info->have_write_global_plte=MagickFalse;
13484 previous_x=mng_info->page.x;
13485 previous_y=mng_info->page.y;
13492 mng_info->page=image->page;
13493 if ((mng_info->page.x != previous_x) ||
13494 (mng_info->page.y != previous_y))
13496 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13497 PNGType(chunk,mng_DEFI);
13498 LogPNGChunk(logging,mng_DEFI,12L);
13499 chunk[4]=0; /* object 0 MSB */
13500 chunk[5]=0; /* object 0 LSB */
13501 chunk[6]=0; /* visible */
13502 chunk[7]=0; /* abstract */
13503 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13504 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13505 (void) WriteBlob(image,16,chunk);
13506 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13511 mng_info->write_mng=write_mng;
13513 if ((int) image->dispose >= 3)
13514 mng_info->framing_mode=3;
13516 if (mng_info->need_fram && mng_info->adjoin &&
13517 ((image->delay != mng_info->delay) ||
13518 (mng_info->framing_mode != mng_info->old_framing_mode)))
13520 if (image->delay == mng_info->delay)
13523 Write a MNG FRAM chunk with the new framing mode.
13525 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13526 PNGType(chunk,mng_FRAM);
13527 LogPNGChunk(logging,mng_FRAM,1L);
13528 chunk[4]=(unsigned char) mng_info->framing_mode;
13529 (void) WriteBlob(image,5,chunk);
13530 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13535 Write a MNG FRAM chunk with the delay.
13537 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13538 PNGType(chunk,mng_FRAM);
13539 LogPNGChunk(logging,mng_FRAM,10L);
13540 chunk[4]=(unsigned char) mng_info->framing_mode;
13541 chunk[5]=0; /* frame name separator (no name) */
13542 chunk[6]=2; /* flag for changing default delay */
13543 chunk[7]=0; /* flag for changing frame timeout */
13544 chunk[8]=0; /* flag for changing frame clipping */
13545 chunk[9]=0; /* flag for changing frame sync_id */
13546 PNGLong(chunk+10,(png_uint_32)
13547 ((mng_info->ticks_per_second*
13548 image->delay)/MagickMax(image->ticks_per_second,1)));
13549 (void) WriteBlob(image,14,chunk);
13550 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13551 mng_info->delay=(png_uint_32) image->delay;
13553 mng_info->old_framing_mode=mng_info->framing_mode;
13556 #if defined(JNG_SUPPORTED)
13557 if (image_info->compression == JPEGCompression)
13562 if (logging != MagickFalse)
13563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13564 " Writing JNG object.");
13565 /* To do: specify the desired alpha compression method. */
13566 write_info=CloneImageInfo(image_info);
13567 write_info->compression=UndefinedCompression;
13568 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13569 write_info=DestroyImageInfo(write_info);
13574 if (logging != MagickFalse)
13575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13576 " Writing PNG object.");
13578 mng_info->need_blob = MagickFalse;
13579 mng_info->ping_preserve_colormap = MagickFalse;
13581 /* We don't want any ancillary chunks written */
13582 mng_info->ping_exclude_bKGD=MagickTrue;
13583 mng_info->ping_exclude_cHRM=MagickTrue;
13584 mng_info->ping_exclude_date=MagickTrue;
13585 mng_info->ping_exclude_EXIF=MagickTrue;
13586 mng_info->ping_exclude_gAMA=MagickTrue;
13587 mng_info->ping_exclude_iCCP=MagickTrue;
13588 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13589 mng_info->ping_exclude_oFFs=MagickTrue;
13590 mng_info->ping_exclude_pHYs=MagickTrue;
13591 mng_info->ping_exclude_sRGB=MagickTrue;
13592 mng_info->ping_exclude_tEXt=MagickTrue;
13593 mng_info->ping_exclude_tRNS=MagickTrue;
13594 mng_info->ping_exclude_vpAg=MagickTrue;
13595 mng_info->ping_exclude_zCCP=MagickTrue;
13596 mng_info->ping_exclude_zTXt=MagickTrue;
13598 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13601 if (status == MagickFalse)
13603 MngInfoFreeStruct(mng_info,&have_mng_structure);
13604 (void) CloseBlob(image);
13605 return(MagickFalse);
13607 (void) CatchImageException(image);
13608 if (GetNextImageInList(image) == (Image *) NULL)
13610 image=SyncNextImageInList(image);
13611 status=SetImageProgress(image,SaveImagesTag,scene++,
13612 GetImageListLength(image));
13614 if (status == MagickFalse)
13617 } while (mng_info->adjoin);
13621 while (GetPreviousImageInList(image) != (Image *) NULL)
13622 image=GetPreviousImageInList(image);
13624 Write the MEND chunk.
13626 (void) WriteBlobMSBULong(image,0x00000000L);
13627 PNGType(chunk,mng_MEND);
13628 LogPNGChunk(logging,mng_MEND,0L);
13629 (void) WriteBlob(image,4,chunk);
13630 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13633 Relinquish resources.
13635 (void) CloseBlob(image);
13636 MngInfoFreeStruct(mng_info,&have_mng_structure);
13638 if (logging != MagickFalse)
13639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13641 return(MagickTrue);
13643 #else /* PNG_LIBPNG_VER > 10011 */
13645 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13648 printf("Your PNG library is too old: You have libpng-%s\n",
13649 PNG_LIBPNG_VER_STRING);
13651 ThrowBinaryException(CoderError,"PNG library is too old",
13652 image_info->filename);
13655 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13657 return(WritePNGImage(image_info,image));
13659 #endif /* PNG_LIBPNG_VER > 10011 */