2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2014 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";
1041 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1050 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1052 switch (ping_colortype)
1070 return "UndefinedColorType";
1075 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1082 #endif /* PNG_LIBPNG_VER > 10011 */
1083 #endif /* MAGICKCORE_PNG_DELEGATE */
1086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1094 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1096 % IsMNG() returns MagickTrue if the image format type, identified by the
1097 % magick string, is MNG.
1099 % The format of the IsMNG method is:
1101 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1103 % A description of each parameter follows:
1105 % o magick: compare image format pattern against these bytes.
1107 % o length: Specifies the length of the magick string.
1111 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1114 return(MagickFalse);
1116 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1119 return(MagickFalse);
1123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1133 % IsJNG() returns MagickTrue if the image format type, identified by the
1134 % magick string, is JNG.
1136 % The format of the IsJNG method is:
1138 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1140 % A description of each parameter follows:
1142 % o magick: compare image format pattern against these bytes.
1144 % o length: Specifies the length of the magick string.
1148 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1151 return(MagickFalse);
1153 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1156 return(MagickFalse);
1160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1170 % IsPNG() returns MagickTrue if the image format type, identified by the
1171 % magick string, is PNG.
1173 % The format of the IsPNG method is:
1175 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1177 % A description of each parameter follows:
1179 % o magick: compare image format pattern against these bytes.
1181 % o length: Specifies the length of the magick string.
1184 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1187 return(MagickFalse);
1189 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1192 return(MagickFalse);
1195 #if defined(MAGICKCORE_PNG_DELEGATE)
1196 #if defined(__cplusplus) || defined(c_plusplus)
1200 #if (PNG_LIBPNG_VER > 10011)
1201 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1206 assert(image != (Image *) NULL);
1207 assert(image->signature == MagickSignature);
1208 buffer[0]=(unsigned char) (value >> 24);
1209 buffer[1]=(unsigned char) (value >> 16);
1210 buffer[2]=(unsigned char) (value >> 8);
1211 buffer[3]=(unsigned char) value;
1212 return((size_t) WriteBlob(image,4,buffer));
1215 static void PNGLong(png_bytep p,png_uint_32 value)
1217 *p++=(png_byte) ((value >> 24) & 0xff);
1218 *p++=(png_byte) ((value >> 16) & 0xff);
1219 *p++=(png_byte) ((value >> 8) & 0xff);
1220 *p++=(png_byte) (value & 0xff);
1223 #if defined(JNG_SUPPORTED)
1224 static void PNGsLong(png_bytep p,png_int_32 value)
1226 *p++=(png_byte) ((value >> 24) & 0xff);
1227 *p++=(png_byte) ((value >> 16) & 0xff);
1228 *p++=(png_byte) ((value >> 8) & 0xff);
1229 *p++=(png_byte) (value & 0xff);
1233 static void PNGShort(png_bytep p,png_uint_16 value)
1235 *p++=(png_byte) ((value >> 8) & 0xff);
1236 *p++=(png_byte) (value & 0xff);
1239 static void PNGType(png_bytep p,png_bytep type)
1241 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1244 static void LogPNGChunk(MagickBooleanType logging, png_bytep type,
1247 if (logging != MagickFalse)
1248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1249 " Writing %c%c%c%c chunk, length: %.20g",
1250 type[0],type[1],type[2],type[3],(double) length);
1252 #endif /* PNG_LIBPNG_VER > 10011 */
1254 #if defined(__cplusplus) || defined(c_plusplus)
1258 #if PNG_LIBPNG_VER > 10011
1260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1264 % R e a d P N G I m a g e %
1268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1270 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1271 % Multiple-image Network Graphics (MNG) image file and returns it. It
1272 % allocates the memory necessary for the new Image structure and returns a
1273 % pointer to the new image or set of images.
1275 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1277 % The format of the ReadPNGImage method is:
1279 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1281 % A description of each parameter follows:
1283 % o image_info: the image info.
1285 % o exception: return any errors or warnings in this structure.
1287 % To do, more or less in chronological order (as of version 5.5.2,
1288 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1290 % Get 16-bit cheap transparency working.
1292 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1294 % Preserve all unknown and not-yet-handled known chunks found in input
1295 % PNG file and copy them into output PNG files according to the PNG
1298 % (At this point, PNG encoding should be in full MNG compliance)
1300 % Provide options for choice of background to use when the MNG BACK
1301 % chunk is not present or is not mandatory (i.e., leave transparent,
1302 % user specified, MNG BACK, PNG bKGD)
1304 % Implement LOOP/ENDL [done, but could do discretionary loops more
1305 % efficiently by linking in the duplicate frames.].
1307 % Decode and act on the MHDR simplicity profile (offer option to reject
1308 % files or attempt to process them anyway when the profile isn't LC or VLC).
1310 % Upgrade to full MNG without Delta-PNG.
1312 % o BACK [done a while ago except for background image ID]
1313 % o MOVE [done 15 May 1999]
1314 % o CLIP [done 15 May 1999]
1315 % o DISC [done 19 May 1999]
1316 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1317 % o SEEK [partially done 19 May 1999 (discard function only)]
1321 % o MNG-level tEXt/iTXt/zTXt
1326 % o iTXt (wait for libpng implementation).
1328 % Use the scene signature to discover when an identical scene is
1329 % being reused, and just point to the original image->exception instead
1330 % of storing another set of pixels. This not specific to MNG
1331 % but could be applied generally.
1333 % Upgrade to full MNG with Delta-PNG.
1335 % JNG tEXt/iTXt/zTXt
1337 % We will not attempt to read files containing the CgBI chunk.
1338 % They are really Xcode files meant for display on the iPhone.
1339 % These are not valid PNG files and it is impossible to recover
1340 % the original PNG from files that have been converted to Xcode-PNG,
1341 % since irretrievable loss of color data has occurred due to the
1342 % use of premultiplied alpha.
1345 #if defined(__cplusplus) || defined(c_plusplus)
1350 This the function that does the actual reading of data. It is
1351 the same as the one supplied in libpng, except that it receives the
1352 datastream from the ReadBlob() function instead of standard input.
1354 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1359 image=(Image *) png_get_io_ptr(png_ptr);
1365 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1366 if (check != length)
1371 (void) FormatLocaleString(msg,MaxTextExtent,
1372 "Expected %.20g bytes; found %.20g bytes",(double) length,
1374 png_warning(png_ptr,msg);
1375 png_error(png_ptr,"Read Exception");
1380 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1381 !defined(PNG_MNG_FEATURES_SUPPORTED)
1382 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1383 * older than libpng-1.0.3a, which was the first to allow the empty
1384 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1385 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1386 * encountered after an empty PLTE, so we have to look ahead for bKGD
1387 * chunks and remove them from the datastream that is passed to libpng,
1388 * and store their contents for later use.
1390 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1405 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1406 image=(Image *) mng_info->image;
1407 while (mng_info->bytes_in_read_buffer && length)
1409 data[i]=mng_info->read_buffer[i];
1410 mng_info->bytes_in_read_buffer--;
1416 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1418 if (check != length)
1419 png_error(png_ptr,"Read Exception");
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_PLTE,4) == 0)
1431 mng_info->found_empty_plte=MagickTrue;
1432 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1434 mng_info->found_empty_plte=MagickFalse;
1435 mng_info->have_saved_bkgd_index=MagickFalse;
1439 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1442 check=(png_size_t) ReadBlob(image,(size_t) length,
1443 (char *) mng_info->read_buffer);
1444 mng_info->read_buffer[4]=0;
1445 mng_info->bytes_in_read_buffer=4;
1446 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1447 if (mng_info->found_empty_plte)
1450 Skip the bKGD data byte and CRC.
1453 ReadBlob(image,5,(char *) mng_info->read_buffer);
1454 check=(png_size_t) ReadBlob(image,(size_t) length,
1455 (char *) mng_info->read_buffer);
1456 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1457 mng_info->have_saved_bkgd_index=MagickTrue;
1458 mng_info->bytes_in_read_buffer=0;
1466 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1471 image=(Image *) png_get_io_ptr(png_ptr);
1477 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1479 if (check != length)
1480 png_error(png_ptr,"WriteBlob Failed");
1484 static void png_flush_data(png_structp png_ptr)
1489 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1490 static int PalettesAreEqual(Image *a,Image *b)
1495 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1496 return((int) MagickFalse);
1498 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1499 return((int) MagickFalse);
1501 if (a->colors != b->colors)
1502 return((int) MagickFalse);
1504 for (i=0; i < (ssize_t) a->colors; i++)
1506 if ((a->colormap[i].red != b->colormap[i].red) ||
1507 (a->colormap[i].green != b->colormap[i].green) ||
1508 (a->colormap[i].blue != b->colormap[i].blue))
1509 return((int) MagickFalse);
1512 return((int) MagickTrue);
1516 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1518 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1519 mng_info->exists[i] && !mng_info->frozen[i])
1521 #ifdef MNG_OBJECT_BUFFERS
1522 if (mng_info->ob[i] != (MngBuffer *) NULL)
1524 if (mng_info->ob[i]->reference_count > 0)
1525 mng_info->ob[i]->reference_count--;
1527 if (mng_info->ob[i]->reference_count == 0)
1529 if (mng_info->ob[i]->image != (Image *) NULL)
1530 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1532 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1535 mng_info->ob[i]=(MngBuffer *) NULL;
1537 mng_info->exists[i]=MagickFalse;
1538 mng_info->invisible[i]=MagickFalse;
1539 mng_info->viewable[i]=MagickFalse;
1540 mng_info->frozen[i]=MagickFalse;
1541 mng_info->x_off[i]=0;
1542 mng_info->y_off[i]=0;
1543 mng_info->object_clip[i].left=0;
1544 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1545 mng_info->object_clip[i].top=0;
1546 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1550 static void MngInfoFreeStruct(MngInfo *mng_info,
1551 MagickBooleanType *have_mng_structure)
1553 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1558 for (i=1; i < MNG_MAX_OBJECTS; i++)
1559 MngInfoDiscardObject(mng_info,i);
1561 if (mng_info->global_plte != (png_colorp) NULL)
1562 mng_info->global_plte=(png_colorp)
1563 RelinquishMagickMemory(mng_info->global_plte);
1565 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1566 *have_mng_structure=MagickFalse;
1570 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1576 if (box.left < box2.left)
1579 if (box.top < box2.top)
1582 if (box.right > box2.right)
1583 box.right=box2.right;
1585 if (box.bottom > box2.bottom)
1586 box.bottom=box2.bottom;
1591 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1597 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1599 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1600 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1601 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1602 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1603 if (delta_type != 0)
1605 box.left+=previous_box.left;
1606 box.right+=previous_box.right;
1607 box.top+=previous_box.top;
1608 box.bottom+=previous_box.bottom;
1614 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1620 Read two ssize_ts from CLON, MOVE or PAST chunk
1622 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1623 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1625 if (delta_type != 0)
1627 pair.a+=previous_pair.a;
1628 pair.b+=previous_pair.b;
1634 static long mng_get_long(unsigned char *p)
1636 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1639 typedef struct _PNGErrorInfo
1648 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1659 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1660 image=error_info->image;
1661 exception=error_info->exception;
1663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1664 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1666 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1667 "`%s'",image->filename);
1669 #if (PNG_LIBPNG_VER < 10500)
1670 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1671 * are building with libpng-1.4.x and can be ignored.
1673 longjmp(ping->jmpbuf,1);
1675 png_longjmp(ping,1);
1679 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1690 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1691 png_error(ping, message);
1693 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1694 image=error_info->image;
1695 exception=error_info->exception;
1696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1697 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1699 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1700 message,"`%s'",image->filename);
1703 #ifdef PNG_USER_MEM_SUPPORTED
1704 #if PNG_LIBPNG_VER >= 10400
1705 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1707 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1711 return((png_voidp) AcquireMagickMemory((size_t) size));
1715 Free a pointer. It is removed from the list at the same time.
1717 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1720 ptr=RelinquishMagickMemory(ptr);
1721 return((png_free_ptr) NULL);
1725 #if defined(__cplusplus) || defined(c_plusplus)
1730 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1731 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1736 register unsigned char
1750 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1751 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1752 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1753 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1754 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1758 /* look for newline */
1762 /* look for length */
1763 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1766 length=(png_uint_32) StringToLong(sp);
1768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1769 " length: %lu",(unsigned long) length);
1771 while (*sp != ' ' && *sp != '\n')
1774 /* allocate space */
1777 png_warning(ping,"invalid profile length");
1778 return(MagickFalse);
1781 profile=BlobToStringInfo((const void *) NULL,length);
1783 if (profile == (StringInfo *) NULL)
1785 png_warning(ping, "unable to copy profile");
1786 return(MagickFalse);
1789 /* copy profile, skipping white space and column 1 "=" signs */
1790 dp=GetStringInfoDatum(profile);
1793 for (i=0; i < (ssize_t) nibbles; i++)
1795 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1799 png_warning(ping, "ran out of profile data");
1800 profile=DestroyStringInfo(profile);
1801 return(MagickFalse);
1807 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1810 (*dp++)+=unhex[(int) *sp++];
1813 We have already read "Raw profile type.
1815 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1816 profile=DestroyStringInfo(profile);
1818 if (image_info->verbose)
1819 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1824 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1825 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1831 /* The unknown chunk structure contains the chunk data:
1836 Note that libpng has already taken care of the CRC handling.
1839 LogMagickEvent(CoderEvent,GetMagickModule(),
1840 " read_vpag_chunk: found %c%c%c%c chunk",
1841 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1843 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1844 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1845 return(0); /* Did not recognize */
1847 /* recognized vpAg */
1849 if (chunk->size != 9)
1850 return(-1); /* Error return */
1852 if (chunk->data[8] != 0)
1853 return(0); /* ImageMagick requires pixel units */
1855 image=(Image *) png_get_user_chunk_ptr(ping);
1857 image->page.width=(size_t) ((chunk->data[0] << 24) |
1858 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1860 image->page.height=(size_t) ((chunk->data[4] << 24) |
1861 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1863 /* Return one of the following: */
1864 /* return(-n); chunk had an error */
1865 /* return(0); did not recognize */
1866 /* return(n); success */
1873 #if defined(PNG_tIME_SUPPORTED)
1874 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1875 ExceptionInfo *exception)
1880 if (png_get_tIME(ping,info,&time))
1885 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1886 time->year,time->month,time->day,time->hour,time->minute,time->second);
1887 SetImageProperty(image,"png:tIME",timestamp,exception);
1893 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1897 % R e a d O n e P N G I m a g e %
1901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1903 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1904 % (minus the 8-byte signature) and returns it. It allocates the memory
1905 % necessary for the new Image structure and returns a pointer to the new
1908 % The format of the ReadOnePNGImage method is:
1910 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1911 % ExceptionInfo *exception)
1913 % A description of each parameter follows:
1915 % o mng_info: Specifies a pointer to a MngInfo structure.
1917 % o image_info: the image info.
1919 % o exception: return any errors or warnings in this structure.
1922 static Image *ReadOnePNGImage(MngInfo *mng_info,
1923 const ImageInfo *image_info, ExceptionInfo *exception)
1925 /* Read one PNG image */
1927 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1940 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
1950 ping_interlace_method,
1951 ping_compression_method,
1965 ping_found_sRGB_cHRM,
1970 *volatile pixel_info;
2008 register unsigned char
2028 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2029 png_byte unused_chunks[]=
2031 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2032 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2033 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2034 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2035 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2036 #if !defined(PNG_tIME_SUPPORTED)
2037 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2039 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2040 /* ignore the APNG chunks */
2041 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2042 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2043 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2048 /* Define these outside of the following "if logging()" block so they will
2049 * show in debuggers.
2052 (void) ConcatenateMagickString(im_vers,
2053 MagickLibVersionText,32);
2054 (void) ConcatenateMagickString(im_vers,
2055 MagickLibAddendum,32);
2058 (void) ConcatenateMagickString(libpng_vers,
2059 PNG_LIBPNG_VER_STRING,32);
2061 (void) ConcatenateMagickString(libpng_runv,
2062 png_get_libpng_ver(NULL),32);
2065 (void) ConcatenateMagickString(zlib_vers,
2068 (void) ConcatenateMagickString(zlib_runv,
2071 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2072 " Enter ReadOnePNGImage()\n"
2073 " IM version = %s\n"
2074 " Libpng version = %s",
2075 im_vers, libpng_vers);
2077 if (logging != MagickFalse)
2079 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2081 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2084 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2086 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2088 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2093 #if (PNG_LIBPNG_VER < 10200)
2094 if (image_info->verbose)
2095 printf("Your PNG library (libpng-%s) is rather old.\n",
2096 PNG_LIBPNG_VER_STRING);
2099 #if (PNG_LIBPNG_VER >= 10400)
2100 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2101 if (image_info->verbose)
2103 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2104 PNG_LIBPNG_VER_STRING);
2105 printf("Please update it.\n");
2111 quantum_info = (QuantumInfo *) NULL;
2112 image=mng_info->image;
2114 if (logging != MagickFalse)
2116 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2117 " Before reading:\n"
2118 " image->alpha_trait=%d"
2119 " image->rendering_intent=%d\n"
2120 " image->colorspace=%d\n"
2122 (int) image->alpha_trait, (int) image->rendering_intent,
2123 (int) image->colorspace, image->gamma);
2125 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2127 /* Set to an out-of-range color unless tRNS chunk is present */
2128 transparent_color.red=65537;
2129 transparent_color.green=65537;
2130 transparent_color.blue=65537;
2131 transparent_color.alpha=65537;
2136 num_raw_profiles = 0;
2138 ping_found_cHRM = MagickFalse;
2139 ping_found_gAMA = MagickFalse;
2140 ping_found_iCCP = MagickFalse;
2141 ping_found_sRGB = MagickFalse;
2142 ping_found_sRGB_cHRM = MagickFalse;
2143 ping_preserve_iCCP = MagickFalse;
2147 Allocate the PNG structures
2149 #ifdef PNG_USER_MEM_SUPPORTED
2150 error_info.image=image;
2151 error_info.exception=exception;
2152 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2153 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2154 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2156 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2157 MagickPNGErrorHandler,MagickPNGWarningHandler);
2159 if (ping == (png_struct *) NULL)
2160 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 ping_info=png_create_info_struct(ping);
2164 if (ping_info == (png_info *) NULL)
2166 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2167 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2170 end_info=png_create_info_struct(ping);
2172 if (end_info == (png_info *) NULL)
2174 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2175 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2178 pixel_info=(MemoryInfo *) NULL;
2180 if (setjmp(png_jmpbuf(ping)))
2183 PNG image is corrupt.
2185 png_destroy_read_struct(&ping,&ping_info,&end_info);
2187 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2188 UnlockSemaphoreInfo(ping_semaphore);
2191 if (pixel_info != (MemoryInfo *) NULL)
2192 pixel_info=RelinquishVirtualMemory(pixel_info);
2194 if (logging != MagickFalse)
2195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2196 " exit ReadOnePNGImage() with error.");
2198 if (image != (Image *) NULL)
2201 return(GetFirstImageInList(image));
2204 /* { For navigation to end of SETJMP-protected block. Within this
2205 * block, use png_error() instead of Throwing an Exception, to ensure
2206 * that libpng is able to clean up, and that the semaphore is unlocked.
2209 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2210 LockSemaphoreInfo(ping_semaphore);
2213 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2214 /* Allow benign errors */
2215 png_set_benign_errors(ping, 1);
2219 Prepare PNG for reading.
2222 mng_info->image_found++;
2223 png_set_sig_bytes(ping,8);
2225 if (LocaleCompare(image_info->magick,"MNG") == 0)
2227 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2228 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2229 png_set_read_fn(ping,image,png_get_data);
2231 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2232 png_permit_empty_plte(ping,MagickTrue);
2233 png_set_read_fn(ping,image,png_get_data);
2235 mng_info->image=image;
2236 mng_info->bytes_in_read_buffer=0;
2237 mng_info->found_empty_plte=MagickFalse;
2238 mng_info->have_saved_bkgd_index=MagickFalse;
2239 png_set_read_fn(ping,mng_info,mng_get_data);
2245 png_set_read_fn(ping,image,png_get_data);
2251 value=GetImageOption(image_info,"profile:skip");
2253 if (IsOptionMember("ICC",value) == MagickFalse)
2256 value=GetImageOption(image_info,"png:preserve-iCCP");
2259 value=GetImageArtifact(image,"png:preserve-iCCP");
2262 ping_preserve_iCCP=MagickTrue;
2264 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2265 /* Don't let libpng check for ICC/sRGB profile because we're going
2266 * to do that anyway. This feature was added at libpng-1.6.12.
2267 * If logging, go ahead and check and issue a warning as appropriate.
2269 if (logging == MagickFalse)
2270 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2273 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2276 png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2280 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2281 /* Ignore unused chunks and all unknown chunks except for vpAg */
2282 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2283 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2285 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2287 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2288 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2289 (int)sizeof(unused_chunks)/5);
2290 /* Callback for other unknown chunks */
2291 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2294 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2295 # if (PNG_LIBPNG_VER >= 10400)
2296 /* Limit the size of the chunk storage cache used for sPLT, text,
2297 * and unknown chunks.
2299 png_set_chunk_cache_max(ping, 32767);
2303 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2304 /* Disable new libpng-1.5.10 feature */
2305 png_set_check_for_invalid_index (ping, 0);
2308 #if (PNG_LIBPNG_VER < 10400)
2309 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2310 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2311 /* Disable thread-unsafe features of pnggccrd */
2312 if (png_access_version_number() >= 10200)
2314 png_uint_32 mmx_disable_mask=0;
2315 png_uint_32 asm_flags;
2317 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2318 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2319 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2320 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2321 asm_flags=png_get_asm_flags(ping);
2322 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2327 png_read_info(ping,ping_info);
2329 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2330 &ping_bit_depth,&ping_color_type,
2331 &ping_interlace_method,&ping_compression_method,
2332 &ping_filter_method);
2334 ping_file_depth = ping_bit_depth;
2336 /* Swap bytes if requested */
2337 if (ping_file_depth == 16)
2342 value=GetImageOption(image_info,"png:swap-bytes");
2345 value=GetImageArtifact(image,"png:swap-bytes");
2351 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2356 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
2357 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2359 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
2360 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2363 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2366 (void) png_get_bKGD(ping, ping_info, &ping_background);
2368 if (ping_bit_depth < 8)
2370 png_set_packing(ping);
2374 image->depth=ping_bit_depth;
2375 image->depth=GetImageQuantumDepth(image,MagickFalse);
2376 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2378 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2379 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2381 image->rendering_intent=UndefinedIntent;
2382 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2383 (void) ResetMagickMemory(&image->chromaticity,0,
2384 sizeof(image->chromaticity));
2387 if (logging != MagickFalse)
2389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2390 " PNG width: %.20g, height: %.20g\n"
2391 " PNG color_type: %d, bit_depth: %d\n"
2392 " PNG compression_method: %d\n"
2393 " PNG interlace_method: %d, filter_method: %d",
2394 (double) ping_width, (double) ping_height,
2395 ping_color_type, ping_bit_depth,
2396 ping_compression_method,
2397 ping_interlace_method,ping_filter_method);
2401 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2403 ping_found_iCCP=MagickTrue;
2404 if (logging != MagickFalse)
2405 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2406 " Found PNG iCCP chunk.");
2409 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2411 ping_found_gAMA=MagickTrue;
2412 if (logging != MagickFalse)
2413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2414 " Found PNG gAMA chunk.");
2417 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2419 ping_found_cHRM=MagickTrue;
2420 if (logging != MagickFalse)
2421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2422 " Found PNG cHRM chunk.");
2425 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2428 ping_found_sRGB=MagickTrue;
2429 if (logging != MagickFalse)
2430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2431 " Found PNG sRGB chunk.");
2434 #ifdef PNG_READ_iCCP_SUPPORTED
2435 if (ping_found_iCCP !=MagickTrue &&
2436 ping_found_sRGB != MagickTrue &&
2437 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2439 ping_found_iCCP=MagickTrue;
2440 if (logging != MagickFalse)
2441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2442 " Found PNG iCCP chunk.");
2445 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2450 #if (PNG_LIBPNG_VER < 10500)
2464 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2467 if (profile_length != 0)
2472 if (logging != MagickFalse)
2473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2474 " Reading PNG iCCP chunk.");
2476 profile=BlobToStringInfo(info,profile_length);
2478 if (profile == (StringInfo *) NULL)
2480 png_warning(ping, "ICC profile is NULL");
2481 profile=DestroyStringInfo(profile);
2485 if (ping_preserve_iCCP == MagickFalse)
2499 length=(png_uint_32) GetStringInfoLength(profile);
2501 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2503 if (length == sRGB_info[icheck].len)
2507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2508 " Got a %lu-byte ICC profile (potentially sRGB)",
2509 (unsigned long) length);
2511 data=GetStringInfoDatum(profile);
2512 profile_crc=crc32(0,data,length);
2514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2515 " with crc=%8x",(unsigned int) profile_crc);
2519 if (profile_crc == sRGB_info[icheck].crc)
2521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522 " It is sRGB with rendering intent = %s",
2523 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2524 sRGB_info[icheck].intent));
2525 if (image->rendering_intent==UndefinedIntent)
2527 image->rendering_intent=
2528 Magick_RenderingIntent_from_PNG_RenderingIntent(
2529 sRGB_info[icheck].intent);
2535 if (sRGB_info[icheck].len == 0)
2537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538 " Got a %lu-byte ICC profile not recognized as sRGB",
2539 (unsigned long) length);
2540 (void) SetImageProfile(image,"icc",profile,exception);
2543 else /* Preserve-iCCP */
2545 (void) SetImageProfile(image,"icc",profile,exception);
2548 profile=DestroyStringInfo(profile);
2554 #if defined(PNG_READ_sRGB_SUPPORTED)
2556 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2559 if (png_get_sRGB(ping,ping_info,&intent))
2561 if (image->rendering_intent == UndefinedIntent)
2562 image->rendering_intent=
2563 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2565 if (logging != MagickFalse)
2566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2567 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2571 else if (mng_info->have_global_srgb)
2573 if (image->rendering_intent == UndefinedIntent)
2574 image->rendering_intent=
2575 Magick_RenderingIntent_from_PNG_RenderingIntent
2576 (mng_info->global_srgb_intent);
2583 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2584 if (mng_info->have_global_gama)
2585 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2587 if (png_get_gAMA(ping,ping_info,&file_gamma))
2589 image->gamma=(float) file_gamma;
2590 if (logging != MagickFalse)
2591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2592 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2596 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2598 if (mng_info->have_global_chrm != MagickFalse)
2600 (void) png_set_cHRM(ping,ping_info,
2601 mng_info->global_chrm.white_point.x,
2602 mng_info->global_chrm.white_point.y,
2603 mng_info->global_chrm.red_primary.x,
2604 mng_info->global_chrm.red_primary.y,
2605 mng_info->global_chrm.green_primary.x,
2606 mng_info->global_chrm.green_primary.y,
2607 mng_info->global_chrm.blue_primary.x,
2608 mng_info->global_chrm.blue_primary.y);
2612 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2614 (void) png_get_cHRM(ping,ping_info,
2615 &image->chromaticity.white_point.x,
2616 &image->chromaticity.white_point.y,
2617 &image->chromaticity.red_primary.x,
2618 &image->chromaticity.red_primary.y,
2619 &image->chromaticity.green_primary.x,
2620 &image->chromaticity.green_primary.y,
2621 &image->chromaticity.blue_primary.x,
2622 &image->chromaticity.blue_primary.y);
2624 ping_found_cHRM=MagickTrue;
2626 if (image->chromaticity.red_primary.x>0.6399f &&
2627 image->chromaticity.red_primary.x<0.6401f &&
2628 image->chromaticity.red_primary.y>0.3299f &&
2629 image->chromaticity.red_primary.y<0.3301f &&
2630 image->chromaticity.green_primary.x>0.2999f &&
2631 image->chromaticity.green_primary.x<0.3001f &&
2632 image->chromaticity.green_primary.y>0.5999f &&
2633 image->chromaticity.green_primary.y<0.6001f &&
2634 image->chromaticity.blue_primary.x>0.1499f &&
2635 image->chromaticity.blue_primary.x<0.1501f &&
2636 image->chromaticity.blue_primary.y>0.0599f &&
2637 image->chromaticity.blue_primary.y<0.0601f &&
2638 image->chromaticity.white_point.x>0.3126f &&
2639 image->chromaticity.white_point.x<0.3128f &&
2640 image->chromaticity.white_point.y>0.3289f &&
2641 image->chromaticity.white_point.y<0.3291f)
2642 ping_found_sRGB_cHRM=MagickTrue;
2645 if (image->rendering_intent != UndefinedIntent)
2647 if (ping_found_sRGB != MagickTrue &&
2648 (ping_found_gAMA != MagickTrue ||
2649 (image->gamma > .45 && image->gamma < .46)) &&
2650 (ping_found_cHRM != MagickTrue ||
2651 ping_found_sRGB_cHRM != MagickFalse) &&
2652 ping_found_iCCP != MagickTrue)
2654 png_set_sRGB(ping,ping_info,
2655 Magick_RenderingIntent_to_PNG_RenderingIntent
2656 (image->rendering_intent));
2657 file_gamma=1.000f/2.200f;
2658 ping_found_sRGB=MagickTrue;
2659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2660 " Setting sRGB as if in input");
2664 #if defined(PNG_oFFs_SUPPORTED)
2665 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2667 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2668 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2670 if (logging != MagickFalse)
2671 if (image->page.x || image->page.y)
2672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2673 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2674 image->page.x,(double) image->page.y);
2677 #if defined(PNG_pHYs_SUPPORTED)
2678 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2680 if (mng_info->have_global_phys)
2682 png_set_pHYs(ping,ping_info,
2683 mng_info->global_x_pixels_per_unit,
2684 mng_info->global_y_pixels_per_unit,
2685 mng_info->global_phys_unit_type);
2692 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2695 Set image resolution.
2697 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2699 image->resolution.x=(double) x_resolution;
2700 image->resolution.y=(double) y_resolution;
2702 if (unit_type == PNG_RESOLUTION_METER)
2704 image->units=PixelsPerCentimeterResolution;
2705 image->resolution.x=(double) x_resolution/100.0;
2706 image->resolution.y=(double) y_resolution/100.0;
2709 if (logging != MagickFalse)
2710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2712 (double) x_resolution,(double) y_resolution,unit_type);
2716 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2721 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2723 if ((number_colors == 0) &&
2724 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2726 if (mng_info->global_plte_length)
2728 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2729 (int) mng_info->global_plte_length);
2731 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2733 if (mng_info->global_trns_length)
2736 "global tRNS has more entries than global PLTE");
2740 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2741 (int) mng_info->global_trns_length,NULL);
2744 #ifdef PNG_READ_bKGD_SUPPORTED
2746 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2747 mng_info->have_saved_bkgd_index ||
2749 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2754 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2755 if (mng_info->have_saved_bkgd_index)
2756 background.index=mng_info->saved_bkgd_index;
2758 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2759 background.index=ping_background->index;
2761 background.red=(png_uint_16)
2762 mng_info->global_plte[background.index].red;
2764 background.green=(png_uint_16)
2765 mng_info->global_plte[background.index].green;
2767 background.blue=(png_uint_16)
2768 mng_info->global_plte[background.index].blue;
2770 background.gray=(png_uint_16)
2771 mng_info->global_plte[background.index].green;
2773 png_set_bKGD(ping,ping_info,&background);
2778 png_error(ping,"No global PLTE in file");
2782 #ifdef PNG_READ_bKGD_SUPPORTED
2783 if (mng_info->have_global_bkgd &&
2784 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2785 image->background_color=mng_info->mng_global_bkgd;
2787 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2792 /* Set image background color.
2793 * Scale background components to 16-bit, then scale
2799 if (ping_file_depth == 1)
2802 else if (ping_file_depth == 2)
2805 else if (ping_file_depth == 4)
2808 if (ping_file_depth <= 8)
2811 ping_background->red *= bkgd_scale;
2812 ping_background->green *= bkgd_scale;
2813 ping_background->blue *= bkgd_scale;
2815 if (logging != MagickFalse)
2817 if (logging != MagickFalse)
2818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2819 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2820 " bkgd_scale=%d. ping_background=(%d,%d,%d).",
2821 ping_background->red,ping_background->green,
2822 ping_background->blue,
2823 bkgd_scale,ping_background->red,
2824 ping_background->green,ping_background->blue);
2827 image->background_color.red=
2828 ScaleShortToQuantum(ping_background->red);
2830 image->background_color.green=
2831 ScaleShortToQuantum(ping_background->green);
2833 image->background_color.blue=
2834 ScaleShortToQuantum(ping_background->blue);
2836 image->background_color.alpha=OpaqueAlpha;
2838 if (logging != MagickFalse)
2839 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2840 " image->background_color=(%.20g,%.20g,%.20g).",
2841 (double) image->background_color.red,
2842 (double) image->background_color.green,
2843 (double) image->background_color.blue);
2845 #endif /* PNG_READ_bKGD_SUPPORTED */
2847 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2850 Image has a tRNS chunk.
2858 if (logging != MagickFalse)
2859 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2860 " Reading PNG tRNS chunk.");
2862 max_sample = (int) ((one << ping_file_depth) - 1);
2864 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2865 (int)ping_trans_color->gray > max_sample) ||
2866 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2867 ((int)ping_trans_color->red > max_sample ||
2868 (int)ping_trans_color->green > max_sample ||
2869 (int)ping_trans_color->blue > max_sample)))
2871 if (logging != MagickFalse)
2872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2873 " Ignoring PNG tRNS chunk with out-of-range sample.");
2874 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2875 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2876 image->alpha_trait=UndefinedPixelTrait;
2883 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2885 /* Scale transparent_color to short */
2886 transparent_color.red= scale_to_short*ping_trans_color->red;
2887 transparent_color.green= scale_to_short*ping_trans_color->green;
2888 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2889 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2891 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2893 if (logging != MagickFalse)
2895 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2896 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
2897 (int) ping_trans_color->gray,(int) transparent_color.alpha);
2900 transparent_color.red=transparent_color.alpha;
2901 transparent_color.green=transparent_color.alpha;
2902 transparent_color.blue=transparent_color.alpha;
2906 #if defined(PNG_READ_sBIT_SUPPORTED)
2907 if (mng_info->have_global_sbit)
2909 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2910 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2913 num_passes=png_set_interlace_handling(ping);
2915 png_read_update_info(ping,ping_info);
2917 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2920 Initialize image structure.
2922 mng_info->image_box.left=0;
2923 mng_info->image_box.right=(ssize_t) ping_width;
2924 mng_info->image_box.top=0;
2925 mng_info->image_box.bottom=(ssize_t) ping_height;
2926 if (mng_info->mng_type == 0)
2928 mng_info->mng_width=ping_width;
2929 mng_info->mng_height=ping_height;
2930 mng_info->frame=mng_info->image_box;
2931 mng_info->clip=mng_info->image_box;
2936 image->page.y=mng_info->y_off[mng_info->object_id];
2939 image->compression=ZipCompression;
2940 image->columns=ping_width;
2941 image->rows=ping_height;
2943 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2944 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2947 image_gamma = image->gamma;
2949 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2950 " image->gamma=%f",(float) image_gamma);
2952 if (image_gamma > 0.75)
2954 /* Set image->rendering_intent to Undefined,
2955 * image->colorspace to GRAY, and reset image->chromaticity.
2957 image->intensity = Rec709LuminancePixelIntensityMethod;
2958 SetImageColorspace(image,GRAYColorspace,exception);
2963 save_rendering_intent = image->rendering_intent;
2965 save_chromaticity = image->chromaticity;
2967 SetImageColorspace(image,GRAYColorspace,exception);
2968 image->rendering_intent = save_rendering_intent;
2969 image->chromaticity = save_chromaticity;
2972 image->gamma = image_gamma;
2975 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2976 " image->colorspace=%d",(int) image->colorspace);
2978 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2979 ((int) ping_bit_depth < 16 &&
2980 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2985 image->storage_class=PseudoClass;
2987 image->colors=one << ping_file_depth;
2988 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2989 if (image->colors > 256)
2992 if (image->colors > 65536L)
2993 image->colors=65536L;
2995 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3000 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3001 image->colors=(size_t) number_colors;
3003 if (logging != MagickFalse)
3004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3005 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3009 if (image->storage_class == PseudoClass)
3012 Initialize image colormap.
3014 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3015 png_error(ping,"Memory allocation failed");
3017 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3022 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3024 for (i=0; i < (ssize_t) number_colors; i++)
3026 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3027 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3028 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3031 for ( ; i < (ssize_t) image->colors; i++)
3033 image->colormap[i].red=0;
3034 image->colormap[i].green=0;
3035 image->colormap[i].blue=0;
3044 scale=(QuantumRange/((1UL << ping_file_depth)-1));
3049 for (i=0; i < (ssize_t) image->colors; i++)
3051 image->colormap[i].red=(Quantum) (i*scale);
3052 image->colormap[i].green=(Quantum) (i*scale);
3053 image->colormap[i].blue=(Quantum) (i*scale);
3058 /* Set some properties for reporting by "identify" */
3063 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3064 ping_interlace_method in value */
3066 (void) FormatLocaleString(msg,MaxTextExtent,
3067 "%d, %d",(int) ping_width, (int) ping_height);
3068 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3070 (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
3071 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3073 (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
3074 (int) ping_color_type,
3075 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3076 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3078 if (ping_interlace_method == 0)
3080 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Not interlaced)",
3081 (int) ping_interlace_method);
3083 else if (ping_interlace_method == 1)
3085 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Adam7 method)",
3086 (int) ping_interlace_method);
3090 (void) FormatLocaleString(msg,MaxTextExtent,"%d (Unknown method)",
3091 (int) ping_interlace_method);
3093 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3095 if (number_colors != 0)
3097 (void) FormatLocaleString(msg,MaxTextExtent,"%d",
3098 (int) number_colors);
3099 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3103 #if defined(PNG_tIME_SUPPORTED)
3104 read_tIME_chunk(image,ping,ping_info,exception);
3109 Read image scanlines.
3111 if (image->delay != 0)
3112 mng_info->scenes_found++;
3114 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3115 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3116 (image_info->first_scene+image_info->number_scenes))))
3118 /* This happens later in non-ping decodes */
3119 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3120 image->storage_class=DirectClass;
3122 if (logging != MagickFalse)
3123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3124 " Skipping PNG image data for scene %.20g",(double)
3125 mng_info->scenes_found-1);
3126 png_destroy_read_struct(&ping,&ping_info,&end_info);
3128 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3129 UnlockSemaphoreInfo(ping_semaphore);
3132 if (logging != MagickFalse)
3133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3134 " exit ReadOnePNGImage().");
3139 if (logging != MagickFalse)
3140 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3141 " Reading PNG IDAT chunk(s)");
3144 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3145 sizeof(*ping_pixels));
3147 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3149 if (pixel_info == (MemoryInfo *) NULL)
3150 png_error(ping,"Memory allocation failed");
3151 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3153 if (logging != MagickFalse)
3154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3155 " Converting PNG pixels to pixel packets");
3157 Convert PNG pixels to pixel packets.
3159 quantum_info=AcquireQuantumInfo(image_info,image);
3161 if (quantum_info == (QuantumInfo *) NULL)
3162 png_error(ping,"Failed to allocate quantum_info");
3164 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3169 found_transparent_pixel;
3171 found_transparent_pixel=MagickFalse;
3173 if (image->storage_class == DirectClass)
3175 for (pass=0; pass < num_passes; pass++)
3178 Convert image to DirectClass pixel packets.
3181 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3182 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3183 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3184 BlendPixelTrait : UndefinedPixelTrait;
3186 for (y=0; y < (ssize_t) image->rows; y++)
3189 row_offset=ping_rowbytes*y;
3194 png_read_row(ping,ping_pixels+row_offset,NULL);
3196 if (pass < num_passes-1)
3199 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3201 if (q == (Quantum *) NULL)
3204 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3205 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3206 GrayQuantum,ping_pixels+row_offset,exception);
3208 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3209 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3210 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3212 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3213 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3214 RGBAQuantum,ping_pixels+row_offset,exception);
3216 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3217 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3218 IndexQuantum,ping_pixels+row_offset,exception);
3220 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3221 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3222 RGBQuantum,ping_pixels+row_offset,exception);
3224 if (found_transparent_pixel == MagickFalse)
3226 /* Is there a transparent pixel in the row? */
3227 if (y== 0 && logging != MagickFalse)
3228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3229 " Looking for cheap transparent pixel");
3231 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3233 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3234 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3235 (GetPixelAlpha(image,q) != OpaqueAlpha))
3237 if (logging != MagickFalse)
3238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3241 found_transparent_pixel = MagickTrue;
3244 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3245 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3246 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3247 transparent_color.red &&
3248 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3249 transparent_color.green &&
3250 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3251 transparent_color.blue))
3253 if (logging != MagickFalse)
3254 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3256 found_transparent_pixel = MagickTrue;
3259 q+=GetPixelChannels(image);
3263 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3265 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3268 if (status == MagickFalse)
3271 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3275 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3277 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3278 if (status == MagickFalse)
3284 else /* image->storage_class != DirectClass */
3286 for (pass=0; pass < num_passes; pass++)
3295 Convert grayscale image to PseudoClass pixel packets.
3297 if (logging != MagickFalse)
3298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3299 " Converting grayscale pixels to pixel packets");
3301 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3302 BlendPixelTrait : UndefinedPixelTrait;
3304 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3305 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3306 sizeof(*quantum_scanline));
3308 if (quantum_scanline == (Quantum *) NULL)
3309 png_error(ping,"Memory allocation failed");
3311 for (y=0; y < (ssize_t) image->rows; y++)
3317 row_offset=ping_rowbytes*y;
3322 png_read_row(ping,ping_pixels+row_offset,NULL);
3324 if (pass < num_passes-1)
3327 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3329 if (q == (Quantum *) NULL)
3332 p=ping_pixels+row_offset;
3335 switch (ping_bit_depth)
3340 if (ping_color_type == 4)
3341 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3345 alpha=ScaleCharToQuantum((unsigned char)*p++);
3347 SetPixelAlpha(image,alpha,q);
3349 if (alpha != OpaqueAlpha)
3350 found_transparent_pixel = MagickTrue;
3352 q+=GetPixelChannels(image);
3356 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3364 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3366 #if (MAGICKCORE_QUANTUM_DEPTH == 16) || (MAGICKCORE_QUANTUM_DEPTH == 32)
3370 if (image->colors > 256)
3371 quantum=((*p++) << 8);
3377 *r=ScaleShortToQuantum(quantum);
3380 if (ping_color_type == 4)
3382 if (image->colors > 256)
3383 quantum=((*p++) << 8);
3389 alpha=ScaleShortToQuantum(quantum);
3390 SetPixelAlpha(image,alpha,q);
3392 if (alpha != OpaqueAlpha)
3393 found_transparent_pixel = MagickTrue;
3395 q+=GetPixelChannels(image);
3398 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3400 p++; /* strip low byte */
3402 if (ping_color_type == 4)
3404 SetPixelAlpha(image,*p++,q);
3406 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3407 found_transparent_pixel = MagickTrue;
3410 q+=GetPixelChannels(image);
3423 Transfer image scanline.
3427 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3429 if (q == (Quantum *) NULL)
3431 for (x=0; x < (ssize_t) image->columns; x++)
3433 SetPixelIndex(image,*r++,q);
3434 q+=GetPixelChannels(image);
3437 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3440 if ((image->previous == (Image *) NULL) && (num_passes == 1))
3442 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3445 if (status == MagickFalse)
3450 if ((image->previous == (Image *) NULL) && (num_passes != 1))
3452 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3454 if (status == MagickFalse)
3458 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3461 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3462 UndefinedPixelTrait;
3464 if (logging != MagickFalse)
3466 if (found_transparent_pixel != MagickFalse)
3467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3468 " Found transparent pixel");
3471 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3472 " No transparent pixel was found");
3474 ping_color_type&=0x03;
3479 if (quantum_info != (QuantumInfo *) NULL)
3480 quantum_info=DestroyQuantumInfo(quantum_info);
3482 if (image->storage_class == PseudoClass)
3487 alpha_trait=image->alpha_trait;
3488 image->alpha_trait=UndefinedPixelTrait;
3489 (void) SyncImage(image,exception);
3490 image->alpha_trait=alpha_trait;
3493 png_read_end(ping,end_info);
3495 if (logging != MagickFalse)
3497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3498 " image->storage_class=%d\n",(int) image->storage_class);
3501 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3502 (ssize_t) image_info->first_scene && image->delay != 0)
3504 png_destroy_read_struct(&ping,&ping_info,&end_info);
3505 pixel_info=RelinquishVirtualMemory(pixel_info);
3507 (void) SetImageBackgroundColor(image,exception);
3508 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3509 UnlockSemaphoreInfo(ping_semaphore);
3511 if (logging != MagickFalse)
3512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3513 " exit ReadOnePNGImage() early.");
3517 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3523 Image has a transparent background.
3525 storage_class=image->storage_class;
3526 image->alpha_trait=BlendPixelTrait;
3528 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3530 if (storage_class == PseudoClass)
3532 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3534 for (x=0; x < ping_num_trans; x++)
3536 image->colormap[x].alpha_trait=BlendPixelTrait;
3537 image->colormap[x].alpha =
3538 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3542 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3544 for (x=0; x < (int) image->colors; x++)
3546 if (ScaleQuantumToShort(image->colormap[x].red) ==
3547 transparent_color.alpha)
3549 image->colormap[x].alpha_trait=BlendPixelTrait;
3550 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3554 (void) SyncImage(image,exception);
3557 #if 1 /* Should have already been done above, but glennrp problem P10
3562 for (y=0; y < (ssize_t) image->rows; y++)
3564 image->storage_class=storage_class;
3565 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3567 if (q == (Quantum *) NULL)
3571 /* Caution: on a Q8 build, this does not distinguish between
3572 * 16-bit colors that differ only in the low byte
3574 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3576 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3577 transparent_color.red &&
3578 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3579 transparent_color.green &&
3580 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3581 transparent_color.blue)
3583 SetPixelAlpha(image,TransparentAlpha,q);
3586 #if 0 /* I have not found a case where this is needed. */
3589 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3593 q+=GetPixelChannels(image);
3596 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3602 image->storage_class=DirectClass;
3605 for (j = 0; j < 2; j++)
3608 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3609 MagickTrue : MagickFalse;
3611 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3612 MagickTrue : MagickFalse;
3614 if (status != MagickFalse)
3615 for (i=0; i < (ssize_t) num_text; i++)
3617 /* Check for a profile */
3619 if (logging != MagickFalse)
3620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3621 " Reading PNG text chunk");
3623 if (memcmp(text[i].key, "Raw profile type ",17) == 0)
3628 value=GetImageOption(image_info,"profile:skip");
3630 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3632 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3635 if (logging != MagickFalse)
3636 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3637 " Read raw profile %s",text[i].key+17);
3641 if (logging != MagickFalse)
3642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3643 " Skipping raw profile %s",text[i].key+17);
3652 length=text[i].text_length;
3653 value=(char *) AcquireQuantumMemory(length+MaxTextExtent,
3655 if (value == (char *) NULL)
3657 png_error(ping,"Memory allocation failed");
3661 (void) ConcatenateMagickString(value,text[i].text,length+2);
3663 /* Don't save "density" or "units" property if we have a pHYs
3666 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3667 (LocaleCompare(text[i].key,"density") != 0 &&
3668 LocaleCompare(text[i].key,"units") != 0))
3669 (void) SetImageProperty(image,text[i].key,value,exception);
3671 if (logging != MagickFalse)
3673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3676 (unsigned long) length,
3680 value=DestroyString(value);
3683 num_text_total += num_text;
3686 #ifdef MNG_OBJECT_BUFFERS
3688 Store the object if necessary.
3690 if (object_id && !mng_info->frozen[object_id])
3692 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3695 create a new object buffer.
3697 mng_info->ob[object_id]=(MngBuffer *)
3698 AcquireMagickMemory(sizeof(MngBuffer));
3700 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3702 mng_info->ob[object_id]->image=(Image *) NULL;
3703 mng_info->ob[object_id]->reference_count=1;
3707 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3708 mng_info->ob[object_id]->frozen)
3710 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3711 png_error(ping,"Memory allocation failed");
3713 if (mng_info->ob[object_id]->frozen)
3714 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3720 if (mng_info->ob[object_id]->image != (Image *) NULL)
3721 mng_info->ob[object_id]->image=DestroyImage
3722 (mng_info->ob[object_id]->image);
3724 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3727 if (mng_info->ob[object_id]->image != (Image *) NULL)
3728 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3731 png_error(ping, "Cloning image for object buffer failed");
3733 if (ping_width > 250000L || ping_height > 250000L)
3734 png_error(ping,"PNG Image dimensions are too large.");
3736 mng_info->ob[object_id]->width=ping_width;
3737 mng_info->ob[object_id]->height=ping_height;
3738 mng_info->ob[object_id]->color_type=ping_color_type;
3739 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3740 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3741 mng_info->ob[object_id]->compression_method=
3742 ping_compression_method;
3743 mng_info->ob[object_id]->filter_method=ping_filter_method;
3745 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3751 Copy the PLTE to the object buffer.
3753 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3754 mng_info->ob[object_id]->plte_length=number_colors;
3756 for (i=0; i < number_colors; i++)
3758 mng_info->ob[object_id]->plte[i]=plte[i];
3763 mng_info->ob[object_id]->plte_length=0;
3768 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3769 * alpha or if a valid tRNS chunk is present, no matter whether there
3770 * is actual transparency present.
3772 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3773 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3774 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3775 BlendPixelTrait : UndefinedPixelTrait;
3777 #if 0 /* I'm not sure what's wrong here but it does not work. */
3778 if (image->alpha_trait != UndefinedPixelTrait)
3780 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3781 (void) SetImageType(image,GrayscaleMatteType,exception);
3783 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3784 (void) SetImageType(image,PaletteMatteType,exception);
3787 (void) SetImageType(image,TrueColorMatteType,exception);
3792 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3793 (void) SetImageType(image,GrayscaleType,exception);
3795 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3796 (void) SetImageType(image,PaletteType,exception);
3799 (void) SetImageType(image,TrueColorType,exception);
3803 /* Set more properties for identify to retrieve */
3808 if (num_text_total != 0)
3810 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3811 (void) FormatLocaleString(msg,MaxTextExtent,
3812 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3813 (void) SetImageProperty(image,"png:text",msg,
3817 if (num_raw_profiles != 0)
3819 (void) FormatLocaleString(msg,MaxTextExtent,
3820 "%d were found", num_raw_profiles);
3821 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3825 if (ping_found_cHRM != MagickFalse)
3827 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3828 "chunk was found (see Chromaticity, above)");
3829 (void) SetImageProperty(image,"png:cHRM",msg,
3833 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3835 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3836 "chunk was found (see Background color, above)");
3837 (void) SetImageProperty(image,"png:bKGD",msg,
3841 (void) FormatLocaleString(msg,MaxTextExtent,"%s",
3844 #if defined(PNG_iCCP_SUPPORTED)
3845 if (ping_found_iCCP != MagickFalse)
3846 (void) SetImageProperty(image,"png:iCCP",msg,
3850 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3851 (void) SetImageProperty(image,"png:tRNS",msg,
3854 #if defined(PNG_sRGB_SUPPORTED)
3855 if (ping_found_sRGB != MagickFalse)
3857 (void) FormatLocaleString(msg,MaxTextExtent,
3860 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3861 (void) SetImageProperty(image,"png:sRGB",msg,
3866 if (ping_found_gAMA != MagickFalse)
3868 (void) FormatLocaleString(msg,MaxTextExtent,
3869 "gamma=%.8g (See Gamma, above)",
3871 (void) SetImageProperty(image,"png:gAMA",msg,
3875 #if defined(PNG_pHYs_SUPPORTED)
3876 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3878 (void) FormatLocaleString(msg,MaxTextExtent,
3879 "x_res=%.10g, y_res=%.10g, units=%d",
3880 (double) x_resolution,(double) y_resolution, unit_type);
3881 (void) SetImageProperty(image,"png:pHYs",msg,
3886 #if defined(PNG_oFFs_SUPPORTED)
3887 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3889 (void) FormatLocaleString(msg,MaxTextExtent,"x_off=%.20g, y_off=%.20g",
3890 (double) image->page.x,(double) image->page.y);
3891 (void) SetImageProperty(image,"png:oFFs",msg,
3896 #if defined(PNG_tIME_SUPPORTED)
3897 read_tIME_chunk(image,ping,end_info,exception);
3900 if ((image->page.width != 0 && image->page.width != image->columns) ||
3901 (image->page.height != 0 && image->page.height != image->rows))
3903 (void) FormatLocaleString(msg,MaxTextExtent,
3904 "width=%.20g, height=%.20g",
3905 (double) image->page.width,(double) image->page.height);
3906 (void) SetImageProperty(image,"png:vpAg",msg,
3912 Relinquish resources.
3914 png_destroy_read_struct(&ping,&ping_info,&end_info);
3916 pixel_info=RelinquishVirtualMemory(pixel_info);
3918 if (logging != MagickFalse)
3919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3920 " exit ReadOnePNGImage()");
3922 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3923 UnlockSemaphoreInfo(ping_semaphore);
3926 /* } for navigation to beginning of SETJMP-protected block, revert to
3927 * Throwing an Exception when an error occurs.
3932 /* end of reading one PNG image */
3935 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3950 magic_number[MaxTextExtent];
3958 assert(image_info != (const ImageInfo *) NULL);
3959 assert(image_info->signature == MagickSignature);
3961 if (image_info->debug != MagickFalse)
3962 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3963 image_info->filename);
3965 assert(exception != (ExceptionInfo *) NULL);
3966 assert(exception->signature == MagickSignature);
3967 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3968 image=AcquireImage(image_info,exception);
3969 mng_info=(MngInfo *) NULL;
3970 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3972 if (status == MagickFalse)
3973 ThrowReaderException(FileOpenError,"UnableToOpenFile");
3976 Verify PNG signature.
3978 count=ReadBlob(image,8,(unsigned char *) magic_number);
3980 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3981 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3984 Allocate a MngInfo structure.
3986 have_mng_structure=MagickFalse;
3987 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3989 if (mng_info == (MngInfo *) NULL)
3990 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3993 Initialize members of the MngInfo structure.
3995 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3996 mng_info->image=image;
3997 have_mng_structure=MagickTrue;
4000 image=ReadOnePNGImage(mng_info,image_info,exception);
4001 MngInfoFreeStruct(mng_info,&have_mng_structure);
4003 if (image == (Image *) NULL)
4005 if (previous != (Image *) NULL)
4007 if (previous->signature != MagickSignature)
4008 ThrowReaderException(CorruptImageError,"CorruptImage");
4010 (void) CloseBlob(previous);
4011 (void) DestroyImageList(previous);
4014 if (logging != MagickFalse)
4015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4016 "exit ReadPNGImage() with error");
4018 return((Image *) NULL);
4021 (void) CloseBlob(image);
4023 if ((image->columns == 0) || (image->rows == 0))
4025 if (logging != MagickFalse)
4026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4027 "exit ReadPNGImage() with error.");
4029 ThrowReaderException(CorruptImageError,"CorruptImage");
4032 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4033 ((image->gamma < .45) || (image->gamma > .46)) &&
4034 !(image->chromaticity.red_primary.x>0.6399f &&
4035 image->chromaticity.red_primary.x<0.6401f &&
4036 image->chromaticity.red_primary.y>0.3299f &&
4037 image->chromaticity.red_primary.y<0.3301f &&
4038 image->chromaticity.green_primary.x>0.2999f &&
4039 image->chromaticity.green_primary.x<0.3001f &&
4040 image->chromaticity.green_primary.y>0.5999f &&
4041 image->chromaticity.green_primary.y<0.6001f &&
4042 image->chromaticity.blue_primary.x>0.1499f &&
4043 image->chromaticity.blue_primary.x<0.1501f &&
4044 image->chromaticity.blue_primary.y>0.0599f &&
4045 image->chromaticity.blue_primary.y<0.0601f &&
4046 image->chromaticity.white_point.x>0.3126f &&
4047 image->chromaticity.white_point.x<0.3128f &&
4048 image->chromaticity.white_point.y>0.3289f &&
4049 image->chromaticity.white_point.y<0.3291f))
4051 SetImageColorspace(image,RGBColorspace,exception);
4054 if (logging != MagickFalse)
4056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4057 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4058 (double) image->page.width,(double) image->page.height,
4059 (double) image->page.x,(double) image->page.y);
4060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4061 " image->colorspace: %d", (int) image->colorspace);
4064 if (logging != MagickFalse)
4065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4072 #if defined(JNG_SUPPORTED)
4074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4078 % R e a d O n e J N G I m a g e %
4082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4084 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4085 % (minus the 8-byte signature) and returns it. It allocates the memory
4086 % necessary for the new Image structure and returns a pointer to the new
4089 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4091 % The format of the ReadOneJNGImage method is:
4093 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4094 % ExceptionInfo *exception)
4096 % A description of each parameter follows:
4098 % o mng_info: Specifies a pointer to a MngInfo structure.
4100 % o image_info: the image info.
4102 % o exception: return any errors or warnings in this structure.
4105 static Image *ReadOneJNGImage(MngInfo *mng_info,
4106 const ImageInfo *image_info, ExceptionInfo *exception)
4133 jng_image_sample_depth,
4134 jng_image_compression_method,
4135 jng_image_interlace_method,
4136 jng_alpha_sample_depth,
4137 jng_alpha_compression_method,
4138 jng_alpha_filter_method,
4139 jng_alpha_interlace_method;
4141 register const Quantum
4151 register unsigned char
4161 jng_alpha_compression_method=0;
4162 jng_alpha_sample_depth=8;
4166 alpha_image=(Image *) NULL;
4167 color_image=(Image *) NULL;
4168 alpha_image_info=(ImageInfo *) NULL;
4169 color_image_info=(ImageInfo *) NULL;
4171 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4172 " Enter ReadOneJNGImage()");
4174 image=mng_info->image;
4176 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4179 Allocate next image structure.
4181 if (logging != MagickFalse)
4182 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4183 " AcquireNextImage()");
4185 AcquireNextImage(image_info,image,exception);
4187 if (GetNextImageInList(image) == (Image *) NULL)
4188 return((Image *) NULL);
4190 image=SyncNextImageInList(image);
4192 mng_info->image=image;
4195 Signature bytes have already been read.
4198 read_JSEP=MagickFalse;
4199 reading_idat=MagickFalse;
4203 type[MaxTextExtent];
4212 Read a new JNG chunk.
4214 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4215 2*GetBlobSize(image));
4217 if (status == MagickFalse)
4221 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
4222 length=ReadBlobMSBLong(image);
4223 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4225 if (logging != MagickFalse)
4226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4227 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4228 type[0],type[1],type[2],type[3],(double) length);
4230 if (length > PNG_UINT_31_MAX || count == 0)
4231 ThrowReaderException(CorruptImageError,"CorruptImage");
4234 chunk=(unsigned char *) NULL;
4238 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4240 if (chunk == (unsigned char *) NULL)
4241 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4243 for (i=0; i < (ssize_t) length; i++)
4244 chunk[i]=(unsigned char) ReadBlobByte(image);
4249 (void) ReadBlobMSBLong(image); /* read crc word */
4251 if (memcmp(type,mng_JHDR,4) == 0)
4255 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4256 (p[2] << 8) | p[3]);
4257 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4258 (p[6] << 8) | p[7]);
4259 if ((jng_width == 0) || (jng_height == 0))
4260 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4261 jng_color_type=p[8];
4262 jng_image_sample_depth=p[9];
4263 jng_image_compression_method=p[10];
4264 jng_image_interlace_method=p[11];
4266 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4269 jng_alpha_sample_depth=p[12];
4270 jng_alpha_compression_method=p[13];
4271 jng_alpha_filter_method=p[14];
4272 jng_alpha_interlace_method=p[15];
4274 if (logging != MagickFalse)
4276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4277 " jng_width: %16lu, jng_height: %16lu\n"
4278 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4279 " jng_image_compression_method:%3d",
4280 (unsigned long) jng_width, (unsigned long) jng_height,
4281 jng_color_type, jng_image_sample_depth,
4282 jng_image_compression_method);
4284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4285 " jng_image_interlace_method: %3d"
4286 " jng_alpha_sample_depth: %3d",
4287 jng_image_interlace_method,
4288 jng_alpha_sample_depth);
4290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4291 " jng_alpha_compression_method:%3d\n"
4292 " jng_alpha_filter_method: %3d\n"
4293 " jng_alpha_interlace_method: %3d",
4294 jng_alpha_compression_method,
4295 jng_alpha_filter_method,
4296 jng_alpha_interlace_method);
4301 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4307 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4308 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4309 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4312 o create color_image
4313 o open color_blob, attached to color_image
4314 o if (color type has alpha)
4315 open alpha_blob, attached to alpha_image
4318 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4320 if (color_image_info == (ImageInfo *) NULL)
4321 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4323 GetImageInfo(color_image_info);
4324 color_image=AcquireImage(color_image_info,exception);
4326 if (color_image == (Image *) NULL)
4327 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4329 if (logging != MagickFalse)
4330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4331 " Creating color_blob.");
4333 (void) AcquireUniqueFilename(color_image->filename);
4334 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4337 if (status == MagickFalse)
4338 return((Image *) NULL);
4340 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4342 alpha_image_info=(ImageInfo *)
4343 AcquireMagickMemory(sizeof(ImageInfo));
4345 if (alpha_image_info == (ImageInfo *) NULL)
4346 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4348 GetImageInfo(alpha_image_info);
4349 alpha_image=AcquireImage(alpha_image_info,exception);
4351 if (alpha_image == (Image *) NULL)
4353 alpha_image=DestroyImage(alpha_image);
4354 ThrowReaderException(ResourceLimitError,
4355 "MemoryAllocationFailed");
4358 if (logging != MagickFalse)
4359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4360 " Creating alpha_blob.");
4362 (void) AcquireUniqueFilename(alpha_image->filename);
4363 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4366 if (status == MagickFalse)
4367 return((Image *) NULL);
4369 if (jng_alpha_compression_method == 0)
4374 if (logging != MagickFalse)
4375 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4376 " Writing IHDR chunk to alpha_blob.");
4378 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4379 "\211PNG\r\n\032\n");
4381 (void) WriteBlobMSBULong(alpha_image,13L);
4382 PNGType(data,mng_IHDR);
4383 LogPNGChunk(logging,mng_IHDR,13L);
4384 PNGLong(data+4,jng_width);
4385 PNGLong(data+8,jng_height);
4386 data[12]=jng_alpha_sample_depth;
4387 data[13]=0; /* color_type gray */
4388 data[14]=0; /* compression method 0 */
4389 data[15]=0; /* filter_method 0 */
4390 data[16]=0; /* interlace_method 0 */
4391 (void) WriteBlob(alpha_image,17,data);
4392 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4395 reading_idat=MagickTrue;
4398 if (memcmp(type,mng_JDAT,4) == 0)
4400 /* Copy chunk to color_image->blob */
4402 if (logging != MagickFalse)
4403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404 " Copying JDAT chunk data to color_blob.");
4406 (void) WriteBlob(color_image,length,chunk);
4409 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4414 if (memcmp(type,mng_IDAT,4) == 0)
4419 /* Copy IDAT header and chunk data to alpha_image->blob */
4421 if (image_info->ping == MagickFalse)
4423 if (logging != MagickFalse)
4424 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4425 " Copying IDAT chunk data to alpha_blob.");
4427 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4428 PNGType(data,mng_IDAT);
4429 LogPNGChunk(logging,mng_IDAT,length);
4430 (void) WriteBlob(alpha_image,4,data);
4431 (void) WriteBlob(alpha_image,length,chunk);
4432 (void) WriteBlobMSBULong(alpha_image,
4433 crc32(crc32(0,data,4),chunk,(uInt) length));
4437 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4442 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4444 /* Copy chunk data to alpha_image->blob */
4446 if (image_info->ping == MagickFalse)
4448 if (logging != MagickFalse)
4449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4450 " Copying JDAA chunk data to alpha_blob.");
4452 (void) WriteBlob(alpha_image,length,chunk);
4456 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4461 if (memcmp(type,mng_JSEP,4) == 0)
4463 read_JSEP=MagickTrue;
4466 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4471 if (memcmp(type,mng_bKGD,4) == 0)
4475 image->background_color.red=ScaleCharToQuantum(p[1]);
4476 image->background_color.green=image->background_color.red;
4477 image->background_color.blue=image->background_color.red;
4482 image->background_color.red=ScaleCharToQuantum(p[1]);
4483 image->background_color.green=ScaleCharToQuantum(p[3]);
4484 image->background_color.blue=ScaleCharToQuantum(p[5]);
4487 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4491 if (memcmp(type,mng_gAMA,4) == 0)
4494 image->gamma=((float) mng_get_long(p))*0.00001;
4496 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4500 if (memcmp(type,mng_cHRM,4) == 0)
4504 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4505 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4506 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4507 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4508 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4509 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4510 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4511 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4514 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4518 if (memcmp(type,mng_sRGB,4) == 0)
4522 image->rendering_intent=
4523 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4524 image->gamma=1.000f/2.200f;
4525 image->chromaticity.red_primary.x=0.6400f;
4526 image->chromaticity.red_primary.y=0.3300f;
4527 image->chromaticity.green_primary.x=0.3000f;
4528 image->chromaticity.green_primary.y=0.6000f;
4529 image->chromaticity.blue_primary.x=0.1500f;
4530 image->chromaticity.blue_primary.y=0.0600f;
4531 image->chromaticity.white_point.x=0.3127f;
4532 image->chromaticity.white_point.y=0.3290f;
4535 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4539 if (memcmp(type,mng_oFFs,4) == 0)
4543 image->page.x=(ssize_t) mng_get_long(p);
4544 image->page.y=(ssize_t) mng_get_long(&p[4]);
4546 if ((int) p[8] != 0)
4548 image->page.x/=10000;
4549 image->page.y/=10000;
4554 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4559 if (memcmp(type,mng_pHYs,4) == 0)
4563 image->resolution.x=(double) mng_get_long(p);
4564 image->resolution.y=(double) mng_get_long(&p[4]);
4565 if ((int) p[8] == PNG_RESOLUTION_METER)
4567 image->units=PixelsPerCentimeterResolution;
4568 image->resolution.x=image->resolution.x/100.0f;
4569 image->resolution.y=image->resolution.y/100.0f;
4573 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4578 if (memcmp(type,mng_iCCP,4) == 0)
4582 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4589 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4591 if (memcmp(type,mng_IEND,4))
4601 Finish up reading image data:
4603 o read main image from color_blob.
4607 o if (color_type has alpha)
4608 if alpha_encoding is PNG
4609 read secondary image from alpha_blob via ReadPNG
4610 if alpha_encoding is JPEG
4611 read secondary image from alpha_blob via ReadJPEG
4615 o copy intensity of secondary image into
4616 alpha samples of main image.
4618 o destroy the secondary image.
4621 (void) CloseBlob(color_image);
4623 if (logging != MagickFalse)
4624 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4625 " Reading jng_image from color_blob.");
4627 assert(color_image_info != (ImageInfo *) NULL);
4628 (void) FormatLocaleString(color_image_info->filename,MaxTextExtent,"%s",
4629 color_image->filename);
4631 color_image_info->ping=MagickFalse; /* To do: avoid this */
4632 jng_image=ReadImage(color_image_info,exception);
4634 if (jng_image == (Image *) NULL)
4635 return((Image *) NULL);
4637 (void) RelinquishUniqueFileResource(color_image->filename);
4638 color_image=DestroyImage(color_image);
4639 color_image_info=DestroyImageInfo(color_image_info);
4641 if (jng_image == (Image *) NULL)
4642 return((Image *) NULL);
4644 if (logging != MagickFalse)
4645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4646 " Copying jng_image pixels to main image.");
4648 image->rows=jng_height;
4649 image->columns=jng_width;
4651 for (y=0; y < (ssize_t) image->rows; y++)
4653 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4654 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4655 for (x=(ssize_t) image->columns; x != 0; x--)
4657 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4658 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4659 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4660 q+=GetPixelChannels(image);
4661 s+=GetPixelChannels(jng_image);
4664 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4668 jng_image=DestroyImage(jng_image);
4670 if (image_info->ping == MagickFalse)
4672 if (jng_color_type >= 12)
4674 if (jng_alpha_compression_method == 0)
4678 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4679 PNGType(data,mng_IEND);
4680 LogPNGChunk(logging,mng_IEND,0L);
4681 (void) WriteBlob(alpha_image,4,data);
4682 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4685 (void) CloseBlob(alpha_image);
4687 if (logging != MagickFalse)
4688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4689 " Reading alpha from alpha_blob.");
4691 (void) FormatLocaleString(alpha_image_info->filename,MaxTextExtent,
4692 "%s",alpha_image->filename);
4694 jng_image=ReadImage(alpha_image_info,exception);
4696 if (jng_image != (Image *) NULL)
4697 for (y=0; y < (ssize_t) image->rows; y++)
4699 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4701 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4703 if (image->alpha_trait != UndefinedPixelTrait)
4704 for (x=(ssize_t) image->columns; x != 0; x--)
4706 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4707 q+=GetPixelChannels(image);
4708 s+=GetPixelChannels(jng_image);
4712 for (x=(ssize_t) image->columns; x != 0; x--)
4714 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4715 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4716 image->alpha_trait=BlendPixelTrait;
4717 q+=GetPixelChannels(image);
4718 s+=GetPixelChannels(jng_image);
4721 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4724 (void) RelinquishUniqueFileResource(alpha_image->filename);
4725 alpha_image=DestroyImage(alpha_image);
4726 alpha_image_info=DestroyImageInfo(alpha_image_info);
4727 if (jng_image != (Image *) NULL)
4728 jng_image=DestroyImage(jng_image);
4732 /* Read the JNG image. */
4734 if (mng_info->mng_type == 0)
4736 mng_info->mng_width=jng_width;
4737 mng_info->mng_height=jng_height;
4740 if (image->page.width == 0 && image->page.height == 0)
4742 image->page.width=jng_width;
4743 image->page.height=jng_height;
4746 if (image->page.x == 0 && image->page.y == 0)
4748 image->page.x=mng_info->x_off[mng_info->object_id];
4749 image->page.y=mng_info->y_off[mng_info->object_id];
4754 image->page.y=mng_info->y_off[mng_info->object_id];
4757 mng_info->image_found++;
4758 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4759 2*GetBlobSize(image));
4761 if (logging != MagickFalse)
4762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4763 " exit ReadOneJNGImage()");
4769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4773 % R e a d J N G I m a g e %
4777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4779 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4780 % (including the 8-byte signature) and returns it. It allocates the memory
4781 % necessary for the new Image structure and returns a pointer to the new
4784 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4786 % The format of the ReadJNGImage method is:
4788 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4791 % A description of each parameter follows:
4793 % o image_info: the image info.
4795 % o exception: return any errors or warnings in this structure.
4799 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4813 magic_number[MaxTextExtent];
4821 assert(image_info != (const ImageInfo *) NULL);
4822 assert(image_info->signature == MagickSignature);
4823 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4824 assert(exception != (ExceptionInfo *) NULL);
4825 assert(exception->signature == MagickSignature);
4826 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4827 image=AcquireImage(image_info,exception);
4828 mng_info=(MngInfo *) NULL;
4829 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4831 if (status == MagickFalse)
4832 return((Image *) NULL);
4834 if (LocaleCompare(image_info->magick,"JNG") != 0)
4835 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4837 /* Verify JNG signature. */
4839 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4841 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4842 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4844 /* Allocate a MngInfo structure. */
4846 have_mng_structure=MagickFalse;
4847 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4849 if (mng_info == (MngInfo *) NULL)
4850 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4852 /* Initialize members of the MngInfo structure. */
4854 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4855 have_mng_structure=MagickTrue;
4857 mng_info->image=image;
4858 image=ReadOneJNGImage(mng_info,image_info,exception);
4859 MngInfoFreeStruct(mng_info,&have_mng_structure);
4861 if (image == (Image *) NULL)
4863 if (logging != MagickFalse)
4864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4865 "exit ReadJNGImage() with error");
4867 return((Image *) NULL);
4869 (void) CloseBlob(image);
4871 if (image->columns == 0 || image->rows == 0)
4873 if (logging != MagickFalse)
4874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4875 "exit ReadJNGImage() with error");
4877 ThrowReaderException(CorruptImageError,"CorruptImage");
4880 if (logging != MagickFalse)
4881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4887 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4890 page_geometry[MaxTextExtent];
4923 #if defined(MNG_INSERT_LAYERS)
4925 mng_background_color;
4928 register unsigned char
4943 #if defined(MNG_INSERT_LAYERS)
4948 volatile unsigned int
4949 #ifdef MNG_OBJECT_BUFFERS
4950 mng_background_object=0,
4952 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4955 default_frame_timeout,
4957 #if defined(MNG_INSERT_LAYERS)
4963 /* These delays are all measured in image ticks_per_second,
4964 * not in MNG ticks_per_second
4967 default_frame_delay,
4971 #if defined(MNG_INSERT_LAYERS)
4980 previous_fb.bottom=0;
4982 previous_fb.right=0;
4984 default_fb.bottom=0;
4988 /* Open image file. */
4990 assert(image_info != (const ImageInfo *) NULL);
4991 assert(image_info->signature == MagickSignature);
4992 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4993 assert(exception != (ExceptionInfo *) NULL);
4994 assert(exception->signature == MagickSignature);
4995 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
4996 image=AcquireImage(image_info,exception);
4997 mng_info=(MngInfo *) NULL;
4998 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5000 if (status == MagickFalse)
5001 return((Image *) NULL);
5003 first_mng_object=MagickFalse;
5005 have_mng_structure=MagickFalse;
5007 /* Allocate a MngInfo structure. */
5009 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5011 if (mng_info == (MngInfo *) NULL)
5012 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5014 /* Initialize members of the MngInfo structure. */
5016 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5017 mng_info->image=image;
5018 have_mng_structure=MagickTrue;
5020 if (LocaleCompare(image_info->magick,"MNG") == 0)
5023 magic_number[MaxTextExtent];
5025 /* Verify MNG signature. */
5026 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5027 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5028 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5030 /* Initialize some nonzero members of the MngInfo structure. */
5031 for (i=0; i < MNG_MAX_OBJECTS; i++)
5033 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5034 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5036 mng_info->exists[0]=MagickTrue;
5039 first_mng_object=MagickTrue;
5041 #if defined(MNG_INSERT_LAYERS)
5042 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5044 default_frame_delay=0;
5045 default_frame_timeout=0;
5048 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5050 skip_to_iend=MagickFalse;
5051 term_chunk_found=MagickFalse;
5052 mng_info->framing_mode=1;
5053 #if defined(MNG_INSERT_LAYERS)
5054 mandatory_back=MagickFalse;
5056 #if defined(MNG_INSERT_LAYERS)
5057 mng_background_color=image->background_color;
5059 default_fb=mng_info->frame;
5060 previous_fb=mng_info->frame;
5064 type[MaxTextExtent];
5066 if (LocaleCompare(image_info->magick,"MNG") == 0)
5075 (void) ConcatenateMagickString(type,"errr",MaxTextExtent);
5076 length=ReadBlobMSBLong(image);
5077 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5079 if (logging != MagickFalse)
5080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5081 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5082 type[0],type[1],type[2],type[3],(double) length);
5084 if (length > PNG_UINT_31_MAX)
5088 ThrowReaderException(CorruptImageError,"CorruptImage");
5091 chunk=(unsigned char *) NULL;
5095 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5097 if (chunk == (unsigned char *) NULL)
5098 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5100 for (i=0; i < (ssize_t) length; i++)
5101 chunk[i]=(unsigned char) ReadBlobByte(image);
5106 (void) ReadBlobMSBLong(image); /* read crc word */
5108 #if !defined(JNG_SUPPORTED)
5109 if (memcmp(type,mng_JHDR,4) == 0)
5111 skip_to_iend=MagickTrue;
5113 if (mng_info->jhdr_warning == 0)
5114 (void) ThrowMagickException(exception,GetMagickModule(),
5115 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5117 mng_info->jhdr_warning++;
5120 if (memcmp(type,mng_DHDR,4) == 0)
5122 skip_to_iend=MagickTrue;
5124 if (mng_info->dhdr_warning == 0)
5125 (void) ThrowMagickException(exception,GetMagickModule(),
5126 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5128 mng_info->dhdr_warning++;
5130 if (memcmp(type,mng_MEND,4) == 0)
5135 if (memcmp(type,mng_IEND,4) == 0)
5136 skip_to_iend=MagickFalse;
5139 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5141 if (logging != MagickFalse)
5142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5148 if (memcmp(type,mng_MHDR,4) == 0)
5150 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5151 (p[2] << 8) | p[3]);
5153 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5154 (p[6] << 8) | p[7]);
5156 if (logging != MagickFalse)
5158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5159 " MNG width: %.20g",(double) mng_info->mng_width);
5160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5161 " MNG height: %.20g",(double) mng_info->mng_height);
5165 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5167 if (mng_info->ticks_per_second == 0)
5168 default_frame_delay=0;
5171 default_frame_delay=1UL*image->ticks_per_second/
5172 mng_info->ticks_per_second;
5174 frame_delay=default_frame_delay;
5180 simplicity=(size_t) mng_get_long(p);
5183 mng_type=1; /* Full MNG */
5185 if ((simplicity != 0) && ((simplicity | 11) == 11))
5186 mng_type=2; /* LC */
5188 if ((simplicity != 0) && ((simplicity | 9) == 9))
5189 mng_type=3; /* VLC */
5191 #if defined(MNG_INSERT_LAYERS)
5193 insert_layers=MagickTrue;
5195 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5197 /* Allocate next image structure. */
5198 AcquireNextImage(image_info,image,exception);
5200 if (GetNextImageInList(image) == (Image *) NULL)
5201 return((Image *) NULL);
5203 image=SyncNextImageInList(image);
5204 mng_info->image=image;
5207 if ((mng_info->mng_width > 65535L) ||
5208 (mng_info->mng_height > 65535L))
5209 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5211 (void) FormatLocaleString(page_geometry,MaxTextExtent,
5212 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5213 mng_info->mng_height);
5215 mng_info->frame.left=0;
5216 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5217 mng_info->frame.top=0;
5218 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5219 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5221 for (i=0; i < MNG_MAX_OBJECTS; i++)
5222 mng_info->object_clip[i]=mng_info->frame;
5224 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5228 if (memcmp(type,mng_TERM,4) == 0)
5239 final_delay=(png_uint_32) mng_get_long(&p[2]);
5240 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5242 if (mng_iterations == PNG_UINT_31_MAX)
5245 image->iterations=mng_iterations;
5246 term_chunk_found=MagickTrue;
5249 if (logging != MagickFalse)
5251 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5252 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5253 repeat,(double) final_delay, (double) image->iterations);
5256 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5259 if (memcmp(type,mng_DEFI,4) == 0)
5262 (void) ThrowMagickException(exception,GetMagickModule(),
5263 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5266 object_id=(p[0] << 8) | p[1];
5268 if (mng_type == 2 && object_id != 0)
5269 (void) ThrowMagickException(exception,GetMagickModule(),
5270 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5273 if (object_id > MNG_MAX_OBJECTS)
5276 Instead of using a warning we should allocate a larger
5277 MngInfo structure and continue.
5279 (void) ThrowMagickException(exception,GetMagickModule(),
5280 CoderError,"object id too large","`%s'",image->filename);
5281 object_id=MNG_MAX_OBJECTS;
5284 if (mng_info->exists[object_id])
5285 if (mng_info->frozen[object_id])
5287 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5288 (void) ThrowMagickException(exception,
5289 GetMagickModule(),CoderError,
5290 "DEFI cannot redefine a frozen MNG object","`%s'",
5295 mng_info->exists[object_id]=MagickTrue;
5298 mng_info->invisible[object_id]=p[2];
5301 Extract object offset info.
5305 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5306 (p[5] << 16) | (p[6] << 8) | p[7]);
5308 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5309 (p[9] << 16) | (p[10] << 8) | p[11]);
5311 if (logging != MagickFalse)
5313 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5314 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5315 object_id,(double) mng_info->x_off[object_id],
5316 object_id,(double) mng_info->y_off[object_id]);
5321 Extract object clipping info.
5324 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5327 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5330 if (memcmp(type,mng_bKGD,4) == 0)
5332 mng_info->have_global_bkgd=MagickFalse;
5336 mng_info->mng_global_bkgd.red=
5337 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5339 mng_info->mng_global_bkgd.green=
5340 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5342 mng_info->mng_global_bkgd.blue=
5343 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5345 mng_info->have_global_bkgd=MagickTrue;
5348 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5351 if (memcmp(type,mng_BACK,4) == 0)
5353 #if defined(MNG_INSERT_LAYERS)
5355 mandatory_back=p[6];
5360 if (mandatory_back && length > 5)
5362 mng_background_color.red=
5363 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5365 mng_background_color.green=
5366 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5368 mng_background_color.blue=
5369 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5371 mng_background_color.alpha=OpaqueAlpha;
5374 #ifdef MNG_OBJECT_BUFFERS
5376 mng_background_object=(p[7] << 8) | p[8];
5379 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5383 if (memcmp(type,mng_PLTE,4) == 0)
5385 /* Read global PLTE. */
5387 if (length && (length < 769))
5389 if (mng_info->global_plte == (png_colorp) NULL)
5390 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5391 sizeof(*mng_info->global_plte));
5393 for (i=0; i < (ssize_t) (length/3); i++)
5395 mng_info->global_plte[i].red=p[3*i];
5396 mng_info->global_plte[i].green=p[3*i+1];
5397 mng_info->global_plte[i].blue=p[3*i+2];
5400 mng_info->global_plte_length=(unsigned int) (length/3);
5403 for ( ; i < 256; i++)
5405 mng_info->global_plte[i].red=i;
5406 mng_info->global_plte[i].green=i;
5407 mng_info->global_plte[i].blue=i;
5411 mng_info->global_plte_length=256;
5414 mng_info->global_plte_length=0;
5416 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5420 if (memcmp(type,mng_tRNS,4) == 0)
5422 /* read global tRNS */
5425 for (i=0; i < (ssize_t) length; i++)
5426 mng_info->global_trns[i]=p[i];
5429 for ( ; i < 256; i++)
5430 mng_info->global_trns[i]=255;
5432 mng_info->global_trns_length=(unsigned int) length;
5433 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5436 if (memcmp(type,mng_gAMA,4) == 0)
5443 igamma=mng_get_long(p);
5444 mng_info->global_gamma=((float) igamma)*0.00001;
5445 mng_info->have_global_gama=MagickTrue;
5449 mng_info->have_global_gama=MagickFalse;
5451 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5455 if (memcmp(type,mng_cHRM,4) == 0)
5457 /* Read global cHRM */
5461 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5462 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5463 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5464 mng_info->global_chrm.red_primary.y=0.00001*
5465 mng_get_long(&p[12]);
5466 mng_info->global_chrm.green_primary.x=0.00001*
5467 mng_get_long(&p[16]);
5468 mng_info->global_chrm.green_primary.y=0.00001*
5469 mng_get_long(&p[20]);
5470 mng_info->global_chrm.blue_primary.x=0.00001*
5471 mng_get_long(&p[24]);
5472 mng_info->global_chrm.blue_primary.y=0.00001*
5473 mng_get_long(&p[28]);
5474 mng_info->have_global_chrm=MagickTrue;
5477 mng_info->have_global_chrm=MagickFalse;
5479 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5483 if (memcmp(type,mng_sRGB,4) == 0)
5490 mng_info->global_srgb_intent=
5491 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5492 mng_info->have_global_srgb=MagickTrue;
5495 mng_info->have_global_srgb=MagickFalse;
5497 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5501 if (memcmp(type,mng_iCCP,4) == 0)
5509 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5514 if (memcmp(type,mng_FRAM,4) == 0)
5517 (void) ThrowMagickException(exception,GetMagickModule(),
5518 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5521 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5522 image->delay=frame_delay;
5524 frame_delay=default_frame_delay;
5525 frame_timeout=default_frame_timeout;
5530 mng_info->framing_mode=p[0];
5532 if (logging != MagickFalse)
5533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5534 " Framing_mode=%d",mng_info->framing_mode);
5538 /* Note the delay and frame clipping boundaries. */
5540 p++; /* framing mode */
5542 while (*p && ((p-chunk) < (ssize_t) length))
5543 p++; /* frame name */
5545 p++; /* frame name terminator */
5547 if ((p-chunk) < (ssize_t) (length-4))
5554 change_delay=(*p++);
5555 change_timeout=(*p++);
5556 change_clipping=(*p++);
5557 p++; /* change_sync */
5561 frame_delay=1UL*image->ticks_per_second*
5564 if (mng_info->ticks_per_second != 0)
5565 frame_delay/=mng_info->ticks_per_second;
5568 frame_delay=PNG_UINT_31_MAX;
5570 if (change_delay == 2)
5571 default_frame_delay=frame_delay;
5575 if (logging != MagickFalse)
5576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5577 " Framing_delay=%.20g",(double) frame_delay);
5582 frame_timeout=1UL*image->ticks_per_second*
5585 if (mng_info->ticks_per_second != 0)
5586 frame_timeout/=mng_info->ticks_per_second;
5589 frame_timeout=PNG_UINT_31_MAX;
5591 if (change_delay == 2)
5592 default_frame_timeout=frame_timeout;
5596 if (logging != MagickFalse)
5597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5598 " Framing_timeout=%.20g",(double) frame_timeout);
5601 if (change_clipping)
5603 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5607 if (logging != MagickFalse)
5608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5609 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5610 (double) fb.left,(double) fb.right,(double) fb.top,
5611 (double) fb.bottom);
5613 if (change_clipping == 2)
5619 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5621 subframe_width=(size_t) (mng_info->clip.right
5622 -mng_info->clip.left);
5624 subframe_height=(size_t) (mng_info->clip.bottom
5625 -mng_info->clip.top);
5627 Insert a background layer behind the frame if framing_mode is 4.
5629 #if defined(MNG_INSERT_LAYERS)
5630 if (logging != MagickFalse)
5631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5632 " subframe_width=%.20g, subframe_height=%.20g",(double)
5633 subframe_width,(double) subframe_height);
5635 if (insert_layers && (mng_info->framing_mode == 4) &&
5636 (subframe_width) && (subframe_height))
5638 /* Allocate next image structure. */
5639 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5641 AcquireNextImage(image_info,image,exception);
5643 if (GetNextImageInList(image) == (Image *) NULL)
5645 image=DestroyImageList(image);
5646 MngInfoFreeStruct(mng_info,&have_mng_structure);
5647 return((Image *) NULL);
5650 image=SyncNextImageInList(image);
5653 mng_info->image=image;
5655 if (term_chunk_found)
5657 image->start_loop=MagickTrue;
5658 image->iterations=mng_iterations;
5659 term_chunk_found=MagickFalse;
5663 image->start_loop=MagickFalse;
5665 image->columns=subframe_width;
5666 image->rows=subframe_height;
5667 image->page.width=subframe_width;
5668 image->page.height=subframe_height;
5669 image->page.x=mng_info->clip.left;
5670 image->page.y=mng_info->clip.top;
5671 image->background_color=mng_background_color;
5672 image->alpha_trait=UndefinedPixelTrait;
5674 (void) SetImageBackgroundColor(image,exception);
5676 if (logging != MagickFalse)
5677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5678 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5679 (double) mng_info->clip.left,(double) mng_info->clip.right,
5680 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5683 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5686 if (memcmp(type,mng_CLIP,4) == 0)
5695 first_object=(p[0] << 8) | p[1];
5696 last_object=(p[2] << 8) | p[3];
5698 for (i=(int) first_object; i <= (int) last_object; i++)
5700 if (mng_info->exists[i] && !mng_info->frozen[i])
5705 box=mng_info->object_clip[i];
5706 mng_info->object_clip[i]=mng_read_box(box,(char) p[4],&p[5]);
5710 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5713 if (memcmp(type,mng_SAVE,4) == 0)
5715 for (i=1; i < MNG_MAX_OBJECTS; i++)
5716 if (mng_info->exists[i])
5718 mng_info->frozen[i]=MagickTrue;
5719 #ifdef MNG_OBJECT_BUFFERS
5720 if (mng_info->ob[i] != (MngBuffer *) NULL)
5721 mng_info->ob[i]->frozen=MagickTrue;
5726 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5731 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5733 /* Read DISC or SEEK. */
5735 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5737 for (i=1; i < MNG_MAX_OBJECTS; i++)
5738 MngInfoDiscardObject(mng_info,i);
5746 for (j=0; j < (ssize_t) length; j+=2)
5748 i=p[j] << 8 | p[j+1];
5749 MngInfoDiscardObject(mng_info,i);
5754 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5759 if (memcmp(type,mng_MOVE,4) == 0)
5767 first_object=(p[0] << 8) | p[1];
5768 last_object=(p[2] << 8) | p[3];
5769 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5771 if (mng_info->exists[i] && !mng_info->frozen[i])
5779 old_pair.a=mng_info->x_off[i];
5780 old_pair.b=mng_info->y_off[i];
5781 new_pair=mng_read_pair(old_pair,(int) p[4],&p[5]);
5782 mng_info->x_off[i]=new_pair.a;
5783 mng_info->y_off[i]=new_pair.b;
5787 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5791 if (memcmp(type,mng_LOOP,4) == 0)
5793 ssize_t loop_iters=1;
5794 loop_level=chunk[0];
5795 mng_info->loop_active[loop_level]=1; /* mark loop active */
5797 /* Record starting point. */
5798 loop_iters=mng_get_long(&chunk[1]);
5800 if (logging != MagickFalse)
5801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5802 " LOOP level %.20g has %.20g iterations ",(double) loop_level,
5803 (double) loop_iters);
5805 if (loop_iters == 0)
5806 skipping_loop=loop_level;
5810 mng_info->loop_jump[loop_level]=TellBlob(image);
5811 mng_info->loop_count[loop_level]=loop_iters;
5814 mng_info->loop_iteration[loop_level]=0;
5815 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5819 if (memcmp(type,mng_ENDL,4) == 0)
5821 loop_level=chunk[0];
5823 if (skipping_loop > 0)
5825 if (skipping_loop == loop_level)
5828 Found end of zero-iteration loop.
5831 mng_info->loop_active[loop_level]=0;
5837 if (mng_info->loop_active[loop_level] == 1)
5839 mng_info->loop_count[loop_level]--;
5840 mng_info->loop_iteration[loop_level]++;
5842 if (logging != MagickFalse)
5843 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5844 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5845 (double) loop_level,(double)
5846 mng_info->loop_count[loop_level]);
5848 if (mng_info->loop_count[loop_level] != 0)
5850 offset=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;
5876 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5880 if (memcmp(type,mng_CLON,4) == 0)
5882 if (mng_info->clon_warning == 0)
5883 (void) ThrowMagickException(exception,GetMagickModule(),
5884 CoderError,"CLON is not implemented yet","`%s'",
5887 mng_info->clon_warning++;
5890 if (memcmp(type,mng_MAGN,4) == 0)
5905 magn_first=(p[0] << 8) | p[1];
5911 magn_last=(p[2] << 8) | p[3];
5914 magn_last=magn_first;
5915 #ifndef MNG_OBJECT_BUFFERS
5916 if (magn_first || magn_last)
5917 if (mng_info->magn_warning == 0)
5919 (void) ThrowMagickException(exception,
5920 GetMagickModule(),CoderError,
5921 "MAGN is not implemented yet for nonzero objects",
5922 "`%s'",image->filename);
5924 mng_info->magn_warning++;
5934 magn_mx=(p[5] << 8) | p[6];
5943 magn_my=(p[7] << 8) | p[8];
5952 magn_ml=(p[9] << 8) | p[10];
5961 magn_mr=(p[11] << 8) | p[12];
5970 magn_mt=(p[13] << 8) | p[14];
5979 magn_mb=(p[15] << 8) | p[16];
5991 magn_methy=magn_methx;
5994 if (magn_methx > 5 || magn_methy > 5)
5995 if (mng_info->magn_warning == 0)
5997 (void) ThrowMagickException(exception,
5998 GetMagickModule(),CoderError,
5999 "Unknown MAGN method in MNG datastream","`%s'",
6002 mng_info->magn_warning++;
6004 #ifdef MNG_OBJECT_BUFFERS
6005 /* Magnify existing objects in the range magn_first to magn_last */
6007 if (magn_first == 0 || magn_last == 0)
6009 /* Save the magnification factors for object 0 */
6010 mng_info->magn_mb=magn_mb;
6011 mng_info->magn_ml=magn_ml;
6012 mng_info->magn_mr=magn_mr;
6013 mng_info->magn_mt=magn_mt;
6014 mng_info->magn_mx=magn_mx;
6015 mng_info->magn_my=magn_my;
6016 mng_info->magn_methx=magn_methx;
6017 mng_info->magn_methy=magn_methy;
6021 if (memcmp(type,mng_PAST,4) == 0)
6023 if (mng_info->past_warning == 0)
6024 (void) ThrowMagickException(exception,GetMagickModule(),
6025 CoderError,"PAST is not implemented yet","`%s'",
6028 mng_info->past_warning++;
6031 if (memcmp(type,mng_SHOW,4) == 0)
6033 if (mng_info->show_warning == 0)
6034 (void) ThrowMagickException(exception,GetMagickModule(),
6035 CoderError,"SHOW is not implemented yet","`%s'",
6038 mng_info->show_warning++;
6041 if (memcmp(type,mng_sBIT,4) == 0)
6044 mng_info->have_global_sbit=MagickFalse;
6048 mng_info->global_sbit.gray=p[0];
6049 mng_info->global_sbit.red=p[0];
6050 mng_info->global_sbit.green=p[1];
6051 mng_info->global_sbit.blue=p[2];
6052 mng_info->global_sbit.alpha=p[3];
6053 mng_info->have_global_sbit=MagickTrue;
6056 if (memcmp(type,mng_pHYs,4) == 0)
6060 mng_info->global_x_pixels_per_unit=
6061 (size_t) mng_get_long(p);
6062 mng_info->global_y_pixels_per_unit=
6063 (size_t) mng_get_long(&p[4]);
6064 mng_info->global_phys_unit_type=p[8];
6065 mng_info->have_global_phys=MagickTrue;
6069 mng_info->have_global_phys=MagickFalse;
6071 if (memcmp(type,mng_pHYg,4) == 0)
6073 if (mng_info->phyg_warning == 0)
6074 (void) ThrowMagickException(exception,GetMagickModule(),
6075 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6077 mng_info->phyg_warning++;
6079 if (memcmp(type,mng_BASI,4) == 0)
6081 skip_to_iend=MagickTrue;
6083 if (mng_info->basi_warning == 0)
6084 (void) ThrowMagickException(exception,GetMagickModule(),
6085 CoderError,"BASI is not implemented yet","`%s'",
6088 mng_info->basi_warning++;
6089 #ifdef MNG_BASI_SUPPORTED
6090 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6091 (p[2] << 8) | p[3]);
6092 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6093 (p[6] << 8) | p[7]);
6094 basi_color_type=p[8];
6095 basi_compression_method=p[9];
6096 basi_filter_type=p[10];
6097 basi_interlace_method=p[11];
6099 basi_red=(p[12] << 8) & p[13];
6105 basi_green=(p[14] << 8) & p[15];
6111 basi_blue=(p[16] << 8) & p[17];
6117 basi_alpha=(p[18] << 8) & p[19];
6121 if (basi_sample_depth == 16)
6128 basi_viewable=p[20];
6134 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6138 if (memcmp(type,mng_IHDR,4)
6139 #if defined(JNG_SUPPORTED)
6140 && memcmp(type,mng_JHDR,4)
6144 /* Not an IHDR or JHDR chunk */
6146 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6151 if (logging != MagickFalse)
6152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6153 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6155 mng_info->exists[object_id]=MagickTrue;
6156 mng_info->viewable[object_id]=MagickTrue;
6158 if (mng_info->invisible[object_id])
6160 if (logging != MagickFalse)
6161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6162 " Skipping invisible object");
6164 skip_to_iend=MagickTrue;
6165 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6168 #if defined(MNG_INSERT_LAYERS)
6170 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6172 image_width=(size_t) mng_get_long(p);
6173 image_height=(size_t) mng_get_long(&p[4]);
6175 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6178 Insert a transparent background layer behind the entire animation
6179 if it is not full screen.
6181 #if defined(MNG_INSERT_LAYERS)
6182 if (insert_layers && mng_type && first_mng_object)
6184 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6185 (image_width < mng_info->mng_width) ||
6186 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6187 (image_height < mng_info->mng_height) ||
6188 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6190 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6193 Allocate next image structure.
6195 AcquireNextImage(image_info,image,exception);
6197 if (GetNextImageInList(image) == (Image *) NULL)
6199 image=DestroyImageList(image);
6200 MngInfoFreeStruct(mng_info,&have_mng_structure);
6201 return((Image *) NULL);
6204 image=SyncNextImageInList(image);
6206 mng_info->image=image;
6208 if (term_chunk_found)
6210 image->start_loop=MagickTrue;
6211 image->iterations=mng_iterations;
6212 term_chunk_found=MagickFalse;
6216 image->start_loop=MagickFalse;
6218 /* Make a background rectangle. */
6221 image->columns=mng_info->mng_width;
6222 image->rows=mng_info->mng_height;
6223 image->page.width=mng_info->mng_width;
6224 image->page.height=mng_info->mng_height;
6227 image->background_color=mng_background_color;
6228 (void) SetImageBackgroundColor(image,exception);
6229 if (logging != MagickFalse)
6230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6231 " Inserted transparent background layer, W=%.20g, H=%.20g",
6232 (double) mng_info->mng_width,(double) mng_info->mng_height);
6236 Insert a background layer behind the upcoming image if
6237 framing_mode is 3, and we haven't already inserted one.
6239 if (insert_layers && (mng_info->framing_mode == 3) &&
6240 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6241 (simplicity & 0x08)))
6243 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6246 Allocate next image structure.
6248 AcquireNextImage(image_info,image,exception);
6250 if (GetNextImageInList(image) == (Image *) NULL)
6252 image=DestroyImageList(image);
6253 MngInfoFreeStruct(mng_info,&have_mng_structure);
6254 return((Image *) NULL);
6257 image=SyncNextImageInList(image);
6260 mng_info->image=image;
6262 if (term_chunk_found)
6264 image->start_loop=MagickTrue;
6265 image->iterations=mng_iterations;
6266 term_chunk_found=MagickFalse;
6270 image->start_loop=MagickFalse;
6273 image->columns=subframe_width;
6274 image->rows=subframe_height;
6275 image->page.width=subframe_width;
6276 image->page.height=subframe_height;
6277 image->page.x=mng_info->clip.left;
6278 image->page.y=mng_info->clip.top;
6279 image->background_color=mng_background_color;
6280 image->alpha_trait=UndefinedPixelTrait;
6281 (void) SetImageBackgroundColor(image,exception);
6283 if (logging != MagickFalse)
6284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6285 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6286 (double) mng_info->clip.left,(double) mng_info->clip.right,
6287 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6289 #endif /* MNG_INSERT_LAYERS */
6290 first_mng_object=MagickFalse;
6292 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6295 Allocate next image structure.
6297 AcquireNextImage(image_info,image,exception);
6299 if (GetNextImageInList(image) == (Image *) NULL)
6301 image=DestroyImageList(image);
6302 MngInfoFreeStruct(mng_info,&have_mng_structure);
6303 return((Image *) NULL);
6306 image=SyncNextImageInList(image);
6308 mng_info->image=image;
6309 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6310 GetBlobSize(image));
6312 if (status == MagickFalse)
6315 if (term_chunk_found)
6317 image->start_loop=MagickTrue;
6318 term_chunk_found=MagickFalse;
6322 image->start_loop=MagickFalse;
6324 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6326 image->delay=frame_delay;
6327 frame_delay=default_frame_delay;
6333 image->page.width=mng_info->mng_width;
6334 image->page.height=mng_info->mng_height;
6335 image->page.x=mng_info->x_off[object_id];
6336 image->page.y=mng_info->y_off[object_id];
6337 image->iterations=mng_iterations;
6340 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6343 if (logging != MagickFalse)
6344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6345 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6348 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6351 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 (IsImageObject(previous) != MagickFalse)
6371 (void) DestroyImageList(previous);
6372 (void) CloseBlob(previous);
6375 MngInfoFreeStruct(mng_info,&have_mng_structure);
6376 return((Image *) NULL);
6379 if (image->columns == 0 || image->rows == 0)
6381 (void) CloseBlob(image);
6382 image=DestroyImageList(image);
6383 MngInfoFreeStruct(mng_info,&have_mng_structure);
6384 return((Image *) NULL);
6387 mng_info->image=image;
6394 if (mng_info->magn_methx || mng_info->magn_methy)
6400 if (logging != MagickFalse)
6401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6402 " Processing MNG MAGN chunk");
6404 if (mng_info->magn_methx == 1)
6406 magnified_width=mng_info->magn_ml;
6408 if (image->columns > 1)
6409 magnified_width += mng_info->magn_mr;
6411 if (image->columns > 2)
6412 magnified_width += (png_uint_32)
6413 ((image->columns-2)*(mng_info->magn_mx));
6418 magnified_width=(png_uint_32) image->columns;
6420 if (image->columns > 1)
6421 magnified_width += mng_info->magn_ml-1;
6423 if (image->columns > 2)
6424 magnified_width += mng_info->magn_mr-1;
6426 if (image->columns > 3)
6427 magnified_width += (png_uint_32)
6428 ((image->columns-3)*(mng_info->magn_mx-1));
6431 if (mng_info->magn_methy == 1)
6433 magnified_height=mng_info->magn_mt;
6435 if (image->rows > 1)
6436 magnified_height += mng_info->magn_mb;
6438 if (image->rows > 2)
6439 magnified_height += (png_uint_32)
6440 ((image->rows-2)*(mng_info->magn_my));
6445 magnified_height=(png_uint_32) image->rows;
6447 if (image->rows > 1)
6448 magnified_height += mng_info->magn_mt-1;
6450 if (image->rows > 2)
6451 magnified_height += mng_info->magn_mb-1;
6453 if (image->rows > 3)
6454 magnified_height += (png_uint_32)
6455 ((image->rows-3)*(mng_info->magn_my-1));
6458 if (magnified_height > image->rows ||
6459 magnified_width > image->columns)
6486 /* Allocate next image structure. */
6488 if (logging != MagickFalse)
6489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6490 " Allocate magnified image");
6492 AcquireNextImage(image_info,image,exception);
6494 if (GetNextImageInList(image) == (Image *) NULL)
6496 image=DestroyImageList(image);
6497 MngInfoFreeStruct(mng_info,&have_mng_structure);
6498 return((Image *) NULL);
6501 large_image=SyncNextImageInList(image);
6503 large_image->columns=magnified_width;
6504 large_image->rows=magnified_height;
6506 magn_methx=mng_info->magn_methx;
6507 magn_methy=mng_info->magn_methy;
6509 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6510 #define QM unsigned short
6511 if (magn_methx != 1 || magn_methy != 1)
6514 Scale pixels to unsigned shorts to prevent
6515 overflow of intermediate values of interpolations
6517 for (y=0; y < (ssize_t) image->rows; y++)
6519 q=GetAuthenticPixels(image,0,y,image->columns,1,
6522 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6524 SetPixelRed(image,ScaleQuantumToShort(
6525 GetPixelRed(image,q)),q);
6526 SetPixelGreen(image,ScaleQuantumToShort(
6527 GetPixelGreen(image,q)),q);
6528 SetPixelBlue(image,ScaleQuantumToShort(
6529 GetPixelBlue(image,q)),q);
6530 SetPixelAlpha(image,ScaleQuantumToShort(
6531 GetPixelAlpha(image,q)),q);
6532 q+=GetPixelChannels(image);
6535 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6543 if (image->alpha_trait != UndefinedPixelTrait)
6544 (void) SetImageBackgroundColor(large_image,exception);
6548 large_image->background_color.alpha=OpaqueAlpha;
6549 (void) SetImageBackgroundColor(large_image,exception);
6551 if (magn_methx == 4)
6554 if (magn_methx == 5)
6557 if (magn_methy == 4)
6560 if (magn_methy == 5)
6564 /* magnify the rows into the right side of the large image */
6566 if (logging != MagickFalse)
6567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6568 " Magnify the rows to %.20g",(double) large_image->rows);
6569 m=(ssize_t) mng_info->magn_mt;
6571 length=(size_t) image->columns*GetPixelChannels(image);
6572 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6573 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6575 if ((prev == (Quantum *) NULL) ||
6576 (next == (Quantum *) NULL))
6578 image=DestroyImageList(image);
6579 MngInfoFreeStruct(mng_info,&have_mng_structure);
6580 ThrowReaderException(ResourceLimitError,
6581 "MemoryAllocationFailed");
6584 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6585 (void) CopyMagickMemory(next,n,length);
6587 for (y=0; y < (ssize_t) image->rows; y++)
6590 m=(ssize_t) mng_info->magn_mt;
6592 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6593 m=(ssize_t) mng_info->magn_mb;
6595 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6596 m=(ssize_t) mng_info->magn_mb;
6598 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6602 m=(ssize_t) mng_info->magn_my;
6608 if (y < (ssize_t) image->rows-1)
6610 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6612 (void) CopyMagickMemory(next,n,length);
6615 for (i=0; i < m; i++, yy++)
6620 assert(yy < (ssize_t) large_image->rows);
6623 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6625 q+=(large_image->columns-image->columns)*
6626 GetPixelChannels(large_image);
6628 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6630 /* To do: get color as function of indexes[x] */
6632 if (image->storage_class == PseudoClass)
6637 if (magn_methy <= 1)
6639 /* replicate previous */
6640 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6641 SetPixelGreen(large_image,GetPixelGreen(image,
6643 SetPixelBlue(large_image,GetPixelBlue(image,
6645 SetPixelAlpha(large_image,GetPixelAlpha(image,
6649 else if (magn_methy == 2 || magn_methy == 4)
6653 SetPixelRed(large_image,GetPixelRed(image,
6655 SetPixelGreen(large_image,GetPixelGreen(image,
6657 SetPixelBlue(large_image,GetPixelBlue(image,
6659 SetPixelAlpha(large_image,GetPixelAlpha(image,
6666 SetPixelRed(large_image,((QM) (((ssize_t)
6667 (2*i*(GetPixelRed(image,n)
6668 -GetPixelRed(image,pixels)+m))/
6670 +GetPixelRed(image,pixels)))),q);
6671 SetPixelGreen(large_image,((QM) (((ssize_t)
6672 (2*i*(GetPixelGreen(image,n)
6673 -GetPixelGreen(image,pixels)+m))/
6675 +GetPixelGreen(image,pixels)))),q);
6676 SetPixelBlue(large_image,((QM) (((ssize_t)
6677 (2*i*(GetPixelBlue(image,n)
6678 -GetPixelBlue(image,pixels)+m))/
6680 +GetPixelBlue(image,pixels)))),q);
6682 if (image->alpha_trait != UndefinedPixelTrait)
6683 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6684 (2*i*(GetPixelAlpha(image,n)
6685 -GetPixelAlpha(image,pixels)+m))
6687 GetPixelAlpha(image,pixels)))),q);
6690 if (magn_methy == 4)
6692 /* Replicate nearest */
6693 if (i <= ((m+1) << 1))
6694 SetPixelAlpha(large_image,GetPixelAlpha(image,
6697 SetPixelAlpha(large_image,GetPixelAlpha(image,
6702 else /* if (magn_methy == 3 || magn_methy == 5) */
6704 /* Replicate nearest */
6705 if (i <= ((m+1) << 1))
6707 SetPixelRed(large_image,GetPixelRed(image,
6709 SetPixelGreen(large_image,GetPixelGreen(image,
6711 SetPixelBlue(large_image,GetPixelBlue(image,
6713 SetPixelAlpha(large_image,GetPixelAlpha(image,
6719 SetPixelRed(large_image,GetPixelRed(image,n),q);
6720 SetPixelGreen(large_image,GetPixelGreen(image,n),
6722 SetPixelBlue(large_image,GetPixelBlue(image,n),
6724 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6728 if (magn_methy == 5)
6730 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6731 (GetPixelAlpha(image,n)
6732 -GetPixelAlpha(image,pixels))
6733 +m))/((ssize_t) (m*2))
6734 +GetPixelAlpha(image,pixels)),q);
6737 n+=GetPixelChannels(image);
6738 q+=GetPixelChannels(large_image);
6739 pixels+=GetPixelChannels(image);
6742 if (SyncAuthenticPixels(large_image,exception) == 0)
6748 prev=(Quantum *) RelinquishMagickMemory(prev);
6749 next=(Quantum *) RelinquishMagickMemory(next);
6751 length=image->columns;
6753 if (logging != MagickFalse)
6754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6755 " Delete original image");
6757 DeleteImageFromList(&image);
6761 mng_info->image=image;
6763 /* magnify the columns */
6764 if (logging != MagickFalse)
6765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6766 " Magnify the columns to %.20g",(double) image->columns);
6768 for (y=0; y < (ssize_t) image->rows; y++)
6773 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6774 pixels=q+(image->columns-length)*GetPixelChannels(image);
6775 n=pixels+GetPixelChannels(image);
6777 for (x=(ssize_t) (image->columns-length);
6778 x < (ssize_t) image->columns; x++)
6780 /* To do: Rewrite using Get/Set***PixelChannel() */
6782 if (x == (ssize_t) (image->columns-length))
6783 m=(ssize_t) mng_info->magn_ml;
6785 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6786 m=(ssize_t) mng_info->magn_mr;
6788 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6789 m=(ssize_t) mng_info->magn_mr;
6791 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6795 m=(ssize_t) mng_info->magn_mx;
6797 for (i=0; i < m; i++)
6799 if (magn_methx <= 1)
6801 /* replicate previous */
6802 SetPixelRed(image,GetPixelRed(image,pixels),q);
6803 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6804 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6805 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6808 else if (magn_methx == 2 || magn_methx == 4)
6812 SetPixelRed(image,GetPixelRed(image,pixels),q);
6813 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6814 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6815 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6818 /* To do: Rewrite using Get/Set***PixelChannel() */
6822 SetPixelRed(image,(QM) ((2*i*(
6823 GetPixelRed(image,n)
6824 -GetPixelRed(image,pixels))+m)
6826 GetPixelRed(image,pixels)),q);
6828 SetPixelGreen(image,(QM) ((2*i*(
6829 GetPixelGreen(image,n)
6830 -GetPixelGreen(image,pixels))+m)
6832 GetPixelGreen(image,pixels)),q);
6834 SetPixelBlue(image,(QM) ((2*i*(
6835 GetPixelBlue(image,n)
6836 -GetPixelBlue(image,pixels))+m)
6838 GetPixelBlue(image,pixels)),q);
6839 if (image->alpha_trait != UndefinedPixelTrait)
6840 SetPixelAlpha(image,(QM) ((2*i*(
6841 GetPixelAlpha(image,n)
6842 -GetPixelAlpha(image,pixels))+m)
6844 GetPixelAlpha(image,pixels)),q);
6847 if (magn_methx == 4)
6849 /* Replicate nearest */
6850 if (i <= ((m+1) << 1))
6852 SetPixelAlpha(image,
6853 GetPixelAlpha(image,pixels)+0,q);
6857 SetPixelAlpha(image,
6858 GetPixelAlpha(image,n)+0,q);
6863 else /* if (magn_methx == 3 || magn_methx == 5) */
6865 /* Replicate nearest */
6866 if (i <= ((m+1) << 1))
6868 SetPixelRed(image,GetPixelRed(image,pixels),q);
6869 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6870 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6871 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6876 SetPixelRed(image,GetPixelRed(image,n),q);
6877 SetPixelGreen(image,GetPixelGreen(image,n),q);
6878 SetPixelBlue(image,GetPixelBlue(image,n),q);
6879 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6882 if (magn_methx == 5)
6885 SetPixelAlpha(image,
6886 (QM) ((2*i*( GetPixelAlpha(image,n)
6887 -GetPixelAlpha(image,pixels))+m)/
6889 +GetPixelAlpha(image,pixels)),q);
6892 q+=GetPixelChannels(image);
6894 n+=GetPixelChannels(image);
6897 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6900 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6901 if (magn_methx != 1 || magn_methy != 1)
6904 Rescale pixels to Quantum
6906 for (y=0; y < (ssize_t) image->rows; y++)
6908 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6910 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6912 SetPixelRed(image,ScaleShortToQuantum(
6913 GetPixelRed(image,q)),q);
6914 SetPixelGreen(image,ScaleShortToQuantum(
6915 GetPixelGreen(image,q)),q);
6916 SetPixelBlue(image,ScaleShortToQuantum(
6917 GetPixelBlue(image,q)),q);
6918 SetPixelAlpha(image,ScaleShortToQuantum(
6919 GetPixelAlpha(image,q)),q);
6920 q+=GetPixelChannels(image);
6923 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6928 if (logging != MagickFalse)
6929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6930 " Finished MAGN processing");
6935 Crop_box is with respect to the upper left corner of the MNG.
6937 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6938 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6939 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6940 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6941 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6942 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6943 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6944 if ((crop_box.left != (mng_info->image_box.left
6945 +mng_info->x_off[object_id])) ||
6946 (crop_box.right != (mng_info->image_box.right
6947 +mng_info->x_off[object_id])) ||
6948 (crop_box.top != (mng_info->image_box.top
6949 +mng_info->y_off[object_id])) ||
6950 (crop_box.bottom != (mng_info->image_box.bottom
6951 +mng_info->y_off[object_id])))
6953 if (logging != MagickFalse)
6954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6955 " Crop the PNG image");
6957 if ((crop_box.left < crop_box.right) &&
6958 (crop_box.top < crop_box.bottom))
6967 Crop_info is with respect to the upper left corner of
6970 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
6971 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
6972 crop_info.width=(size_t) (crop_box.right-crop_box.left);
6973 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
6974 image->page.width=image->columns;
6975 image->page.height=image->rows;
6978 im=CropImage(image,&crop_info,exception);
6980 if (im != (Image *) NULL)
6982 image->columns=im->columns;
6983 image->rows=im->rows;
6984 im=DestroyImage(im);
6985 image->page.width=image->columns;
6986 image->page.height=image->rows;
6987 image->page.x=crop_box.left;
6988 image->page.y=crop_box.top;
6995 No pixels in crop area. The MNG spec still requires
6996 a layer, though, so make a single transparent pixel in
6997 the top left corner.
7002 (void) SetImageBackgroundColor(image,exception);
7003 image->page.width=1;
7004 image->page.height=1;
7009 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7010 image=mng_info->image;
7014 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7015 /* PNG does not handle depths greater than 16 so reduce it even
7018 if (image->depth > 16)
7022 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7023 if (image->depth > 8)
7025 /* To do: fill low byte properly */
7029 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7033 if (image_info->number_scenes != 0)
7035 if (mng_info->scenes_found >
7036 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7040 if (logging != MagickFalse)
7041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7042 " Finished reading image datastream.");
7044 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7046 (void) CloseBlob(image);
7048 if (logging != MagickFalse)
7049 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7050 " Finished reading all image datastreams.");
7052 #if defined(MNG_INSERT_LAYERS)
7053 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7054 (mng_info->mng_height))
7057 Insert a background layer if nothing else was found.
7059 if (logging != MagickFalse)
7060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7061 " No images found. Inserting a background layer.");
7063 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7066 Allocate next image structure.
7068 AcquireNextImage(image_info,image,exception);
7069 if (GetNextImageInList(image) == (Image *) NULL)
7071 image=DestroyImageList(image);
7072 MngInfoFreeStruct(mng_info,&have_mng_structure);
7074 if (logging != MagickFalse)
7075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7076 " Allocation failed, returning NULL.");
7078 return((Image *) NULL);
7080 image=SyncNextImageInList(image);
7082 image->columns=mng_info->mng_width;
7083 image->rows=mng_info->mng_height;
7084 image->page.width=mng_info->mng_width;
7085 image->page.height=mng_info->mng_height;
7088 image->background_color=mng_background_color;
7089 image->alpha_trait=UndefinedPixelTrait;
7091 if (image_info->ping == MagickFalse)
7092 (void) SetImageBackgroundColor(image,exception);
7094 mng_info->image_found++;
7097 image->iterations=mng_iterations;
7099 if (mng_iterations == 1)
7100 image->start_loop=MagickTrue;
7102 while (GetPreviousImageInList(image) != (Image *) NULL)
7105 if (image_count > 10*mng_info->image_found)
7107 if (logging != MagickFalse)
7108 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7110 (void) ThrowMagickException(exception,GetMagickModule(),
7111 CoderError,"Linked list is corrupted, beginning of list not found",
7112 "`%s'",image_info->filename);
7114 return((Image *) NULL);
7117 image=GetPreviousImageInList(image);
7119 if (GetNextImageInList(image) == (Image *) NULL)
7121 if (logging != MagickFalse)
7122 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7124 (void) ThrowMagickException(exception,GetMagickModule(),
7125 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7126 image_info->filename);
7130 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7131 GetNextImageInList(image) ==
7134 if (logging != MagickFalse)
7135 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7136 " First image null");
7138 (void) ThrowMagickException(exception,GetMagickModule(),
7139 CoderError,"image->next for first image is NULL but shouldn't be.",
7140 "`%s'",image_info->filename);
7143 if (mng_info->image_found == 0)
7145 if (logging != MagickFalse)
7146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7147 " No visible images found.");
7149 (void) ThrowMagickException(exception,GetMagickModule(),
7150 CoderError,"No visible images in file","`%s'",image_info->filename);
7152 if (image != (Image *) NULL)
7153 image=DestroyImageList(image);
7155 MngInfoFreeStruct(mng_info,&have_mng_structure);
7156 return((Image *) NULL);
7159 if (mng_info->ticks_per_second)
7160 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7161 final_delay/mng_info->ticks_per_second;
7164 image->start_loop=MagickTrue;
7166 /* Find final nonzero image delay */
7167 final_image_delay=0;
7169 while (GetNextImageInList(image) != (Image *) NULL)
7172 final_image_delay=image->delay;
7174 image=GetNextImageInList(image);
7177 if (final_delay < final_image_delay)
7178 final_delay=final_image_delay;
7180 image->delay=final_delay;
7182 if (logging != MagickFalse)
7183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7184 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7185 (double) final_delay);
7187 if (logging != MagickFalse)
7193 image=GetFirstImageInList(image);
7195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7196 " Before coalesce:");
7198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7199 " scene 0 delay=%.20g",(double) image->delay);
7201 while (GetNextImageInList(image) != (Image *) NULL)
7203 image=GetNextImageInList(image);
7204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7205 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7209 image=GetFirstImageInList(image);
7210 #ifdef MNG_COALESCE_LAYERS
7220 if (logging != MagickFalse)
7221 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7224 next_image=CoalesceImages(image,exception);
7226 if (next_image == (Image *) NULL)
7227 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7229 image=DestroyImageList(image);
7232 for (next=image; next != (Image *) NULL; next=next_image)
7234 next->page.width=mng_info->mng_width;
7235 next->page.height=mng_info->mng_height;
7238 next->scene=scene++;
7239 next_image=GetNextImageInList(next);
7241 if (next_image == (Image *) NULL)
7244 if (next->delay == 0)
7247 next_image->previous=GetPreviousImageInList(next);
7248 if (GetPreviousImageInList(next) == (Image *) NULL)
7251 next->previous->next=next_image;
7252 next=DestroyImage(next);
7258 while (GetNextImageInList(image) != (Image *) NULL)
7259 image=GetNextImageInList(image);
7261 image->dispose=BackgroundDispose;
7263 if (logging != MagickFalse)
7269 image=GetFirstImageInList(image);
7271 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7272 " After coalesce:");
7274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7275 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7276 (double) image->dispose);
7278 while (GetNextImageInList(image) != (Image *) NULL)
7280 image=GetNextImageInList(image);
7282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7283 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7284 (double) image->delay,(double) image->dispose);
7288 image=GetFirstImageInList(image);
7289 MngInfoFreeStruct(mng_info,&have_mng_structure);
7290 have_mng_structure=MagickFalse;
7292 if (logging != MagickFalse)
7293 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7295 return(GetFirstImageInList(image));
7297 #else /* PNG_LIBPNG_VER > 10011 */
7298 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7300 printf("Your PNG library is too old: You have libpng-%s\n",
7301 PNG_LIBPNG_VER_STRING);
7303 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7304 "PNG library is too old","`%s'",image_info->filename);
7306 return(Image *) NULL;
7309 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7311 return(ReadPNGImage(image_info,exception));
7313 #endif /* PNG_LIBPNG_VER > 10011 */
7317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7321 % R e g i s t e r P N G I m a g e %
7325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7327 % RegisterPNGImage() adds properties for the PNG image format to
7328 % the list of supported formats. The properties include the image format
7329 % tag, a method to read and/or write the format, whether the format
7330 % supports the saving of more than one frame to the same file or blob,
7331 % whether the format supports native in-memory I/O, and a brief
7332 % description of the format.
7334 % The format of the RegisterPNGImage method is:
7336 % size_t RegisterPNGImage(void)
7339 ModuleExport size_t RegisterPNGImage(void)
7342 version[MaxTextExtent];
7350 "See http://www.libpng.org/ for details about the PNG format."
7355 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7361 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7367 #if defined(PNG_LIBPNG_VER_STRING)
7368 (void) ConcatenateMagickString(version,"libpng ",MaxTextExtent);
7369 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MaxTextExtent);
7371 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7373 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7374 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7379 entry=SetMagickInfo("MNG");
7380 entry->seekable_stream=MagickTrue; /* To do: eliminate this. */
7382 #if defined(MAGICKCORE_PNG_DELEGATE)
7383 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7384 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7387 entry->magick=(IsImageFormatHandler *) IsMNG;
7388 entry->description=ConstantString("Multiple-image Network Graphics");
7390 if (*version != '\0')
7391 entry->version=ConstantString(version);
7393 entry->mime_type=ConstantString("video/x-mng");
7394 entry->module=ConstantString("PNG");
7395 entry->note=ConstantString(MNGNote);
7396 (void) RegisterMagickInfo(entry);
7398 entry=SetMagickInfo("PNG");
7400 #if defined(MAGICKCORE_PNG_DELEGATE)
7401 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7402 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7405 entry->magick=(IsImageFormatHandler *) IsPNG;
7406 entry->adjoin=MagickFalse;
7407 entry->description=ConstantString("Portable Network Graphics");
7408 entry->mime_type=ConstantString("image/png");
7409 entry->module=ConstantString("PNG");
7411 if (*version != '\0')
7412 entry->version=ConstantString(version);
7414 entry->note=ConstantString(PNGNote);
7415 (void) RegisterMagickInfo(entry);
7417 entry=SetMagickInfo("PNG8");
7419 #if defined(MAGICKCORE_PNG_DELEGATE)
7420 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7421 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7424 entry->magick=(IsImageFormatHandler *) IsPNG;
7425 entry->adjoin=MagickFalse;
7426 entry->description=ConstantString(
7427 "8-bit indexed with optional binary transparency");
7428 entry->mime_type=ConstantString("image/png");
7429 entry->module=ConstantString("PNG");
7430 (void) RegisterMagickInfo(entry);
7432 entry=SetMagickInfo("PNG24");
7435 #if defined(ZLIB_VERSION)
7436 (void) ConcatenateMagickString(version,"zlib ",MaxTextExtent);
7437 (void) ConcatenateMagickString(version,ZLIB_VERSION,MaxTextExtent);
7439 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7441 (void) ConcatenateMagickString(version,",",MaxTextExtent);
7442 (void) ConcatenateMagickString(version,zlib_version,MaxTextExtent);
7446 if (*version != '\0')
7447 entry->version=ConstantString(version);
7449 #if defined(MAGICKCORE_PNG_DELEGATE)
7450 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7451 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7454 entry->magick=(IsImageFormatHandler *) IsPNG;
7455 entry->adjoin=MagickFalse;
7456 entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
7457 entry->mime_type=ConstantString("image/png");
7458 entry->module=ConstantString("PNG");
7459 (void) RegisterMagickInfo(entry);
7461 entry=SetMagickInfo("PNG32");
7463 #if defined(MAGICKCORE_PNG_DELEGATE)
7464 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7465 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7468 entry->magick=(IsImageFormatHandler *) IsPNG;
7469 entry->adjoin=MagickFalse;
7470 entry->description=ConstantString("opaque or transparent 32-bit RGBA");
7471 entry->mime_type=ConstantString("image/png");
7472 entry->module=ConstantString("PNG");
7473 (void) RegisterMagickInfo(entry);
7475 entry=SetMagickInfo("PNG48");
7477 #if defined(MAGICKCORE_PNG_DELEGATE)
7478 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7479 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7482 entry->magick=(IsImageFormatHandler *) IsPNG;
7483 entry->adjoin=MagickFalse;
7484 entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
7485 entry->mime_type=ConstantString("image/png");
7486 entry->module=ConstantString("PNG");
7487 (void) RegisterMagickInfo(entry);
7489 entry=SetMagickInfo("PNG64");
7491 #if defined(MAGICKCORE_PNG_DELEGATE)
7492 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7493 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7496 entry->magick=(IsImageFormatHandler *) IsPNG;
7497 entry->adjoin=MagickFalse;
7498 entry->description=ConstantString("opaque or transparent 64-bit RGBA");
7499 entry->mime_type=ConstantString("image/png");
7500 entry->module=ConstantString("PNG");
7501 (void) RegisterMagickInfo(entry);
7503 entry=SetMagickInfo("PNG00");
7505 #if defined(MAGICKCORE_PNG_DELEGATE)
7506 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7507 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7510 entry->magick=(IsImageFormatHandler *) IsPNG;
7511 entry->adjoin=MagickFalse;
7512 entry->description=ConstantString(
7513 "PNG inheriting bit-depth and color-type from original");
7514 entry->mime_type=ConstantString("image/png");
7515 entry->module=ConstantString("PNG");
7516 (void) RegisterMagickInfo(entry);
7518 entry=SetMagickInfo("JNG");
7520 #if defined(JNG_SUPPORTED)
7521 #if defined(MAGICKCORE_PNG_DELEGATE)
7522 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7523 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7527 entry->magick=(IsImageFormatHandler *) IsJNG;
7528 entry->adjoin=MagickFalse;
7529 entry->description=ConstantString("JPEG Network Graphics");
7530 entry->mime_type=ConstantString("image/x-jng");
7531 entry->module=ConstantString("PNG");
7532 entry->note=ConstantString(JNGNote);
7533 (void) RegisterMagickInfo(entry);
7535 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7536 ping_semaphore=AcquireSemaphoreInfo();
7539 return(MagickImageCoderSignature);
7543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7547 % U n r e g i s t e r P N G I m a g e %
7551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7553 % UnregisterPNGImage() removes format registrations made by the
7554 % PNG module from the list of supported formats.
7556 % The format of the UnregisterPNGImage method is:
7558 % UnregisterPNGImage(void)
7561 ModuleExport void UnregisterPNGImage(void)
7563 (void) UnregisterMagickInfo("MNG");
7564 (void) UnregisterMagickInfo("PNG");
7565 (void) UnregisterMagickInfo("PNG8");
7566 (void) UnregisterMagickInfo("PNG24");
7567 (void) UnregisterMagickInfo("PNG32");
7568 (void) UnregisterMagickInfo("PNG48");
7569 (void) UnregisterMagickInfo("PNG64");
7570 (void) UnregisterMagickInfo("PNG00");
7571 (void) UnregisterMagickInfo("JNG");
7573 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7574 if (ping_semaphore != (SemaphoreInfo *) NULL)
7575 RelinquishSemaphoreInfo(&ping_semaphore);
7579 #if defined(MAGICKCORE_PNG_DELEGATE)
7580 #if PNG_LIBPNG_VER > 10011
7582 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7586 % W r i t e M N G I m a g e %
7590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7592 % WriteMNGImage() writes an image in the Portable Network Graphics
7593 % Group's "Multiple-image Network Graphics" encoded image format.
7595 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7597 % The format of the WriteMNGImage method is:
7599 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7600 % Image *image,ExceptionInfo *exception)
7602 % A description of each parameter follows.
7604 % o image_info: the image info.
7606 % o image: The image.
7608 % o exception: return any errors or warnings in this structure.
7610 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7611 % "To do" under ReadPNGImage):
7613 % Preserve all unknown and not-yet-handled known chunks found in input
7614 % PNG file and copy them into output PNG files according to the PNG
7617 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7619 % Improve selection of color type (use indexed-colour or indexed-colour
7620 % with tRNS when 256 or fewer unique RGBA values are present).
7622 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7623 % This will be complicated if we limit ourselves to generating MNG-LC
7624 % files. For now we ignore disposal method 3 and simply overlay the next
7627 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7628 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7629 % [mostly done 15 June 1999 but still need to take care of tRNS]
7631 % Check for identical sRGB and replace with a global sRGB (and remove
7632 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7633 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7634 % local gAMA/cHRM with local sRGB if appropriate).
7636 % Check for identical sBIT chunks and write global ones.
7638 % Provide option to skip writing the signature tEXt chunks.
7640 % Use signatures to detect identical objects and reuse the first
7641 % instance of such objects instead of writing duplicate objects.
7643 % Use a smaller-than-32k value of compression window size when
7646 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7647 % ancillary text chunks and save profiles.
7649 % Provide an option to force LC files (to ensure exact framing rate)
7652 % Provide an option to force VLC files instead of LC, even when offsets
7653 % are present. This will involve expanding the embedded images with a
7654 % transparent region at the top and/or left.
7658 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7659 png_info *ping_info, unsigned char *profile_type, unsigned char
7660 *profile_description, unsigned char *profile_data, png_uint_32 length)
7679 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7681 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7684 if (image_info->verbose)
7686 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7687 (char *) profile_type, (double) length);
7690 #if PNG_LIBPNG_VER >= 10400
7691 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7693 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7695 description_length=(png_uint_32) strlen((const char *) profile_description);
7696 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7697 + description_length);
7698 #if PNG_LIBPNG_VER >= 10400
7699 text[0].text=(png_charp) png_malloc(ping,
7700 (png_alloc_size_t) allocated_length);
7701 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7703 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7704 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7706 text[0].key[0]='\0';
7707 (void) ConcatenateMagickString(text[0].key,
7708 "Raw profile type ",MaxTextExtent);
7709 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7713 (void) CopyMagickString(dp,(const char *) profile_description,
7715 dp+=description_length;
7717 (void) FormatLocaleString(dp,allocated_length-
7718 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7721 for (i=0; i < (ssize_t) length; i++)
7725 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7726 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7731 text[0].text_length=(png_size_t) (dp-text[0].text);
7732 text[0].compression=image_info->compression == NoCompression ||
7733 (image_info->compression == UndefinedCompression &&
7734 text[0].text_length < 128) ? -1 : 0;
7736 if (text[0].text_length <= allocated_length)
7737 png_set_text(ping,ping_info,text,1);
7739 png_free(ping,text[0].text);
7740 png_free(ping,text[0].key);
7741 png_free(ping,text);
7744 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7745 const char *string, MagickBooleanType logging)
7758 ResetImageProfileIterator(image);
7760 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7762 profile=GetImageProfile(image,name);
7764 if (profile != (const StringInfo *) NULL)
7769 if (LocaleNCompare(name,string,11) == 0)
7771 if (logging != MagickFalse)
7772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7773 " Found %s profile",name);
7775 ping_profile=CloneStringInfo(profile);
7776 data=GetStringInfoDatum(ping_profile),
7777 length=(png_uint_32) GetStringInfoLength(ping_profile);
7782 (void) WriteBlobMSBULong(image,length-5); /* data length */
7783 (void) WriteBlob(image,length-1,data+1);
7784 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7785 ping_profile=DestroyStringInfo(ping_profile);
7789 name=GetNextImageProfile(image);
7795 #if defined(PNG_tIME_SUPPORTED)
7796 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7797 const char *date,ExceptionInfo *exception)
7813 if (date != (const char *) NULL)
7815 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7818 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7819 "Invalid date format specified for png:tIME","`%s'",
7823 ptime.year=(png_uint_16) year;
7824 ptime.month=(png_byte) month;
7825 ptime.day=(png_byte) day;
7826 ptime.hour=(png_byte) hour;
7827 ptime.minute=(png_byte) minute;
7828 ptime.second=(png_byte) second;
7833 png_convert_from_time_t(&ptime,ttime);
7835 png_set_tIME(ping,info,&ptime);
7839 /* Write one PNG image */
7840 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7841 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7872 ping_trans_alpha[256];
7900 ping_have_cheap_transparency,
7913 /* ping_exclude_EXIF, */
7916 /* ping_exclude_iTXt, */
7922 /* ping_exclude_tRNS, */
7924 ping_exclude_zCCP, /* hex-encoded iCCP */
7927 ping_preserve_colormap,
7929 ping_need_colortype_warning,
7937 *volatile pixel_info;
7956 ping_interlace_method,
7957 ping_compression_method,
7974 number_semitransparent,
7976 ping_pHYs_unit_type;
7979 ping_pHYs_x_resolution,
7980 ping_pHYs_y_resolution;
7982 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
7983 " Enter WriteOnePNGImage()");
7985 image = CloneImage(IMimage,0,0,MagickFalse,exception);
7986 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
7987 if (image_info == (ImageInfo *) NULL)
7988 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
7990 /* Define these outside of the following "if logging()" block so they will
7991 * show in debuggers.
7994 (void) ConcatenateMagickString(im_vers,
7995 MagickLibVersionText,MaxTextExtent);
7996 (void) ConcatenateMagickString(im_vers,
7997 MagickLibAddendum,MaxTextExtent);
8000 (void) ConcatenateMagickString(libpng_vers,
8001 PNG_LIBPNG_VER_STRING,32);
8003 (void) ConcatenateMagickString(libpng_runv,
8004 png_get_libpng_ver(NULL),32);
8007 (void) ConcatenateMagickString(zlib_vers,
8010 (void) ConcatenateMagickString(zlib_runv,
8013 if (logging != MagickFalse)
8015 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8017 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8019 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8021 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8024 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8026 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8028 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8033 /* Initialize some stuff */
8036 ping_interlace_method=0,
8037 ping_compression_method=0,
8038 ping_filter_method=0,
8041 ping_background.red = 0;
8042 ping_background.green = 0;
8043 ping_background.blue = 0;
8044 ping_background.gray = 0;
8045 ping_background.index = 0;
8047 ping_trans_color.red=0;
8048 ping_trans_color.green=0;
8049 ping_trans_color.blue=0;
8050 ping_trans_color.gray=0;
8052 ping_pHYs_unit_type = 0;
8053 ping_pHYs_x_resolution = 0;
8054 ping_pHYs_y_resolution = 0;
8056 ping_have_blob=MagickFalse;
8057 ping_have_cheap_transparency=MagickFalse;
8058 ping_have_color=MagickTrue;
8059 ping_have_non_bw=MagickTrue;
8060 ping_have_PLTE=MagickFalse;
8061 ping_have_bKGD=MagickFalse;
8062 ping_have_iCCP=MagickFalse;
8063 ping_have_pHYs=MagickFalse;
8064 ping_have_sRGB=MagickFalse;
8065 ping_have_tRNS=MagickFalse;
8067 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8068 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8069 ping_exclude_date=mng_info->ping_exclude_date;
8070 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8071 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8072 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8073 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8074 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8075 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8076 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8077 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8078 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8079 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8080 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8081 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8082 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8084 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8085 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8086 ping_need_colortype_warning = MagickFalse;
8088 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8089 * i.e., eliminate the ICC profile and set image->rendering_intent.
8090 * Note that this will not involve any changes to the actual pixels
8091 * but merely passes information to applications that read the resulting
8094 * To do: recognize other variants of the sRGB profile, using the CRC to
8095 * verify all recognized variants including the 7 already known.
8097 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8099 * Use something other than image->rendering_intent to record the fact
8100 * that the sRGB profile was found.
8102 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8103 * profile. Record the Blackpoint Compensation, if any.
8105 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8113 ResetImageProfileIterator(image);
8114 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8116 profile=GetImageProfile(image,name);
8118 if (profile != (StringInfo *) NULL)
8120 if ((LocaleCompare(name,"ICC") == 0) ||
8121 (LocaleCompare(name,"ICM") == 0))
8136 length=(png_uint_32) GetStringInfoLength(profile);
8138 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8140 if (length == sRGB_info[icheck].len)
8144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8145 " Got a %lu-byte ICC profile (potentially sRGB)",
8146 (unsigned long) length);
8148 data=GetStringInfoDatum(profile);
8149 profile_crc=crc32(0,data,length);
8151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8152 " with crc=%8x",(unsigned int) profile_crc);
8156 if (profile_crc == sRGB_info[icheck].crc)
8158 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8159 " It is sRGB with rendering intent = %s",
8160 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8161 sRGB_info[icheck].intent));
8162 if (image->rendering_intent==UndefinedIntent)
8164 image->rendering_intent=
8165 Magick_RenderingIntent_from_PNG_RenderingIntent(
8166 sRGB_info[icheck].intent);
8168 ping_exclude_iCCP = MagickTrue;
8169 ping_exclude_zCCP = MagickTrue;
8170 ping_have_sRGB = MagickTrue;
8175 if (sRGB_info[icheck].len == 0)
8176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8177 " Got a %lu-byte ICC profile not recognized as sRGB",
8178 (unsigned long) length);
8181 name=GetNextImageProfile(image);
8186 number_semitransparent = 0;
8187 number_transparent = 0;
8189 if (logging != MagickFalse)
8191 if (image->storage_class == UndefinedClass)
8192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8193 " image->storage_class=UndefinedClass");
8194 if (image->storage_class == DirectClass)
8195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8196 " image->storage_class=DirectClass");
8197 if (image->storage_class == PseudoClass)
8198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 " image->storage_class=PseudoClass");
8200 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8201 " image->taint=MagickTrue":
8202 " image->taint=MagickFalse");
8205 if (image->storage_class == PseudoClass &&
8206 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8207 mng_info->write_png48 || mng_info->write_png64 ||
8208 (mng_info->write_png_colortype != 1 &&
8209 mng_info->write_png_colortype != 5)))
8211 (void) SyncImage(image,exception);
8212 image->storage_class = DirectClass;
8215 if (ping_preserve_colormap == MagickFalse)
8217 if (image->storage_class != PseudoClass && image->colormap != NULL)
8219 /* Free the bogus colormap; it can cause trouble later */
8220 if (logging != MagickFalse)
8221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8222 " Freeing bogus colormap");
8223 (void) RelinquishMagickMemory(image->colormap);
8224 image->colormap=NULL;
8228 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8229 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8232 Sometimes we get PseudoClass images whose RGB values don't match
8233 the colors in the colormap. This code syncs the RGB values.
8235 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8236 (void) SyncImage(image,exception);
8238 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8239 if (image->depth > 8)
8241 if (logging != MagickFalse)
8242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8243 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8249 /* Respect the -depth option */
8250 if (image->depth < 4)
8255 if (image->depth > 2)
8257 /* Scale to 4-bit */
8258 LBR04PacketRGBO(image->background_color);
8260 for (y=0; y < (ssize_t) image->rows; y++)
8262 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8264 if (r == (Quantum *) NULL)
8267 for (x=0; x < (ssize_t) image->columns; x++)
8270 r+=GetPixelChannels(image);
8273 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8277 if (image->storage_class == PseudoClass && image->colormap != NULL)
8279 for (i=0; i < (ssize_t) image->colors; i++)
8281 LBR04PacketRGBO(image->colormap[i]);
8285 else if (image->depth > 1)
8287 /* Scale to 2-bit */
8288 LBR02PacketRGBO(image->background_color);
8290 for (y=0; y < (ssize_t) image->rows; y++)
8292 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8294 if (r == (Quantum *) NULL)
8297 for (x=0; x < (ssize_t) image->columns; x++)
8300 r+=GetPixelChannels(image);
8303 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8307 if (image->storage_class == PseudoClass && image->colormap != NULL)
8309 for (i=0; i < (ssize_t) image->colors; i++)
8311 LBR02PacketRGBO(image->colormap[i]);
8317 /* Scale to 1-bit */
8318 LBR01PacketRGBO(image->background_color);
8320 for (y=0; y < (ssize_t) image->rows; y++)
8322 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8324 if (r == (Quantum *) NULL)
8327 for (x=0; x < (ssize_t) image->columns; x++)
8330 r+=GetPixelChannels(image);
8333 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8337 if (image->storage_class == PseudoClass && image->colormap != NULL)
8339 for (i=0; i < (ssize_t) image->colors; i++)
8341 LBR01PacketRGBO(image->colormap[i]);
8347 /* To do: set to next higher multiple of 8 */
8348 if (image->depth < 8)
8351 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8352 /* PNG does not handle depths greater than 16 so reduce it even
8355 if (image->depth > 8)
8359 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8360 if (image->depth > 8)
8362 /* To do: fill low byte properly */
8366 if (image->depth == 16 && mng_info->write_png_depth != 16)
8367 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8371 image_colors = (int) image->colors;
8372 number_opaque = (int) image->colors;
8373 number_transparent = 0;
8374 number_semitransparent = 0;
8376 if (mng_info->write_png_colortype &&
8377 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8378 mng_info->write_png_colortype < 4 &&
8379 image->alpha_trait == UndefinedPixelTrait)))
8381 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8382 * are not going to need the result.
8384 if (mng_info->write_png_colortype == 1 ||
8385 mng_info->write_png_colortype == 5)
8386 ping_have_color=MagickFalse;
8388 if (image->alpha_trait != UndefinedPixelTrait)
8390 number_transparent = 2;
8391 number_semitransparent = 1;
8395 if (mng_info->write_png_colortype < 7)
8399 * Normally we run this just once, but in the case of writing PNG8
8400 * we reduce the transparency to binary and run again, then if there
8401 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8402 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8403 * palette. Then (To do) we take care of a final reduction that is only
8404 * needed if there are still 256 colors present and one of them has both
8405 * transparent and opaque instances.
8408 tried_332 = MagickFalse;
8409 tried_333 = MagickFalse;
8410 tried_444 = MagickFalse;
8415 * Sometimes we get DirectClass images that have 256 colors or fewer.
8416 * This code will build a colormap.
8418 * Also, sometimes we get PseudoClass images with an out-of-date
8419 * colormap. This code will replace the colormap with a new one.
8420 * Sometimes we get PseudoClass images that have more than 256 colors.
8421 * This code will delete the colormap and change the image to
8424 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8425 * even though it sometimes contains left-over non-opaque values.
8427 * Also we gather some information (number of opaque, transparent,
8428 * and semitransparent pixels, and whether the image has any non-gray
8429 * pixels or only black-and-white pixels) that we might need later.
8431 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8432 * we need to check for bogus non-opaque values, at least.
8440 semitransparent[260],
8443 register const Quantum
8450 if (logging != MagickFalse)
8451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8452 " Enter BUILD_PALETTE:");
8454 if (logging != MagickFalse)
8456 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8457 " image->columns=%.20g",(double) image->columns);
8458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8459 " image->rows=%.20g",(double) image->rows);
8460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8461 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8462 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8463 " image->depth=%.20g",(double) image->depth);
8465 if (image->storage_class == PseudoClass && image->colormap != NULL)
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8468 " Original colormap:");
8469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8470 " i (red,green,blue,alpha)");
8472 for (i=0; i < 256; i++)
8474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8475 " %d (%d,%d,%d,%d)",
8477 (int) image->colormap[i].red,
8478 (int) image->colormap[i].green,
8479 (int) image->colormap[i].blue,
8480 (int) image->colormap[i].alpha);
8483 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8488 " %d (%d,%d,%d,%d)",
8490 (int) image->colormap[i].red,
8491 (int) image->colormap[i].green,
8492 (int) image->colormap[i].blue,
8493 (int) image->colormap[i].alpha);
8498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8499 " image->colors=%d",(int) image->colors);
8501 if (image->colors == 0)
8502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8503 " (zero means unknown)");
8505 if (ping_preserve_colormap == MagickFalse)
8506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8507 " Regenerate the colormap");
8512 number_semitransparent = 0;
8513 number_transparent = 0;
8515 for (y=0; y < (ssize_t) image->rows; y++)
8517 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8519 if (q == (Quantum *) NULL)
8522 for (x=0; x < (ssize_t) image->columns; x++)
8524 if (image->alpha_trait == UndefinedPixelTrait ||
8525 GetPixelAlpha(image,q) == OpaqueAlpha)
8527 if (number_opaque < 259)
8529 if (number_opaque == 0)
8531 GetPixelInfoPixel(image, q, opaque);
8532 opaque[0].alpha=OpaqueAlpha;
8536 for (i=0; i< (ssize_t) number_opaque; i++)
8538 if (IsPixelEquivalent(image,q, opaque+i))
8542 if (i == (ssize_t) number_opaque && number_opaque < 259)
8545 GetPixelInfoPixel(image, q, opaque+i);
8546 opaque[i].alpha=OpaqueAlpha;
8550 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8552 if (number_transparent < 259)
8554 if (number_transparent == 0)
8556 GetPixelInfoPixel(image, q, transparent);
8557 ping_trans_color.red=(unsigned short)
8558 GetPixelRed(image,q);
8559 ping_trans_color.green=(unsigned short)
8560 GetPixelGreen(image,q);
8561 ping_trans_color.blue=(unsigned short)
8562 GetPixelBlue(image,q);
8563 ping_trans_color.gray=(unsigned short)
8564 GetPixelGray(image,q);
8565 number_transparent = 1;
8568 for (i=0; i< (ssize_t) number_transparent; i++)
8570 if (IsPixelEquivalent(image,q, transparent+i))
8574 if (i == (ssize_t) number_transparent &&
8575 number_transparent < 259)
8577 number_transparent++;
8578 GetPixelInfoPixel(image,q,transparent+i);
8584 if (number_semitransparent < 259)
8586 if (number_semitransparent == 0)
8588 GetPixelInfoPixel(image,q,semitransparent);
8589 number_semitransparent = 1;
8592 for (i=0; i< (ssize_t) number_semitransparent; i++)
8594 if (IsPixelEquivalent(image,q, semitransparent+i)
8595 && GetPixelAlpha(image,q) ==
8596 semitransparent[i].alpha)
8600 if (i == (ssize_t) number_semitransparent &&
8601 number_semitransparent < 259)
8603 number_semitransparent++;
8604 GetPixelInfoPixel(image, q, semitransparent+i);
8608 q+=GetPixelChannels(image);
8612 if (mng_info->write_png8 == MagickFalse &&
8613 ping_exclude_bKGD == MagickFalse)
8615 /* Add the background color to the palette, if it
8616 * isn't already there.
8618 if (logging != MagickFalse)
8620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8621 " Check colormap for background (%d,%d,%d)",
8622 (int) image->background_color.red,
8623 (int) image->background_color.green,
8624 (int) image->background_color.blue);
8626 for (i=0; i<number_opaque; i++)
8628 if (opaque[i].red == image->background_color.red &&
8629 opaque[i].green == image->background_color.green &&
8630 opaque[i].blue == image->background_color.blue)
8633 if (number_opaque < 259 && i == number_opaque)
8635 opaque[i] = image->background_color;
8636 ping_background.index = i;
8638 if (logging != MagickFalse)
8640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8641 " background_color index is %d",(int) i);
8645 else if (logging != MagickFalse)
8646 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8647 " No room in the colormap to add background color");
8650 image_colors=number_opaque+number_transparent+number_semitransparent;
8652 if (logging != MagickFalse)
8654 if (image_colors > 256)
8655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8656 " image has more than 256 colors");
8659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8660 " image has %d colors",image_colors);
8663 if (ping_preserve_colormap != MagickFalse)
8666 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8668 ping_have_color=MagickFalse;
8669 ping_have_non_bw=MagickFalse;
8671 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8673 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8674 "incompatible colorspace");
8675 ping_have_color=MagickTrue;
8676 ping_have_non_bw=MagickTrue;
8679 if(image_colors > 256)
8681 for (y=0; y < (ssize_t) image->rows; y++)
8683 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8685 if (q == (Quantum *) NULL)
8689 for (x=0; x < (ssize_t) image->columns; x++)
8691 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8692 GetPixelRed(image,s) != GetPixelBlue(image,s))
8694 ping_have_color=MagickTrue;
8695 ping_have_non_bw=MagickTrue;
8698 s+=GetPixelChannels(image);
8701 if (ping_have_color != MagickFalse)
8704 /* Worst case is black-and-white; we are looking at every
8708 if (ping_have_non_bw == MagickFalse)
8711 for (x=0; x < (ssize_t) image->columns; x++)
8713 if (GetPixelRed(image,s) != 0 &&
8714 GetPixelRed(image,s) != QuantumRange)
8716 ping_have_non_bw=MagickTrue;
8719 s+=GetPixelChannels(image);
8726 if (image_colors < 257)
8732 * Initialize image colormap.
8735 if (logging != MagickFalse)
8736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8737 " Sort the new colormap");
8739 /* Sort palette, transparent first */;
8743 for (i=0; i<number_transparent; i++)
8744 colormap[n++] = transparent[i];
8746 for (i=0; i<number_semitransparent; i++)
8747 colormap[n++] = semitransparent[i];
8749 for (i=0; i<number_opaque; i++)
8750 colormap[n++] = opaque[i];
8752 ping_background.index +=
8753 (number_transparent + number_semitransparent);
8755 /* image_colors < 257; search the colormap instead of the pixels
8756 * to get ping_have_color and ping_have_non_bw
8760 if (ping_have_color == MagickFalse)
8762 if (colormap[i].red != colormap[i].green ||
8763 colormap[i].red != colormap[i].blue)
8765 ping_have_color=MagickTrue;
8766 ping_have_non_bw=MagickTrue;
8771 if (ping_have_non_bw == MagickFalse)
8773 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8774 ping_have_non_bw=MagickTrue;
8778 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8779 (number_transparent == 0 && number_semitransparent == 0)) &&
8780 (((mng_info->write_png_colortype-1) ==
8781 PNG_COLOR_TYPE_PALETTE) ||
8782 (mng_info->write_png_colortype == 0)))
8784 if (logging != MagickFalse)
8786 if (n != (ssize_t) image_colors)
8787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8788 " image_colors (%d) and n (%d) don't match",
8791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8792 " AcquireImageColormap");
8795 image->colors = image_colors;
8797 if (AcquireImageColormap(image,image_colors,exception) ==
8799 ThrowWriterException(ResourceLimitError,
8800 "MemoryAllocationFailed");
8802 for (i=0; i< (ssize_t) image_colors; i++)
8803 image->colormap[i] = colormap[i];
8805 if (logging != MagickFalse)
8807 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8808 " image->colors=%d (%d)",
8809 (int) image->colors, image_colors);
8811 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8812 " Update the pixel indexes");
8815 /* Sync the pixel indices with the new colormap */
8817 for (y=0; y < (ssize_t) image->rows; y++)
8819 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8821 if (q == (Quantum *) NULL)
8824 for (x=0; x < (ssize_t) image->columns; x++)
8826 for (i=0; i< (ssize_t) image_colors; i++)
8828 if ((image->alpha_trait == UndefinedPixelTrait ||
8829 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8830 image->colormap[i].red == GetPixelRed(image,q) &&
8831 image->colormap[i].green == GetPixelGreen(image,q) &&
8832 image->colormap[i].blue == GetPixelBlue(image,q))
8834 SetPixelIndex(image,i,q);
8838 q+=GetPixelChannels(image);
8841 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8847 if (logging != MagickFalse)
8849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8850 " image->colors=%d", (int) image->colors);
8852 if (image->colormap != NULL)
8854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8855 " i (red,green,blue,alpha)");
8857 for (i=0; i < (ssize_t) image->colors; i++)
8859 if (i < 300 || i >= (ssize_t) image->colors - 10)
8861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8862 " %d (%d,%d,%d,%d)",
8864 (int) image->colormap[i].red,
8865 (int) image->colormap[i].green,
8866 (int) image->colormap[i].blue,
8867 (int) image->colormap[i].alpha);
8872 if (number_transparent < 257)
8873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8874 " number_transparent = %d",
8875 number_transparent);
8878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8879 " number_transparent > 256");
8881 if (number_opaque < 257)
8882 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8883 " number_opaque = %d",
8887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8888 " number_opaque > 256");
8890 if (number_semitransparent < 257)
8891 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8892 " number_semitransparent = %d",
8893 number_semitransparent);
8896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8897 " number_semitransparent > 256");
8899 if (ping_have_non_bw == MagickFalse)
8900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8901 " All pixels and the background are black or white");
8903 else if (ping_have_color == MagickFalse)
8904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8905 " All pixels and the background are gray");
8908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8909 " At least one pixel or the background is non-gray");
8911 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8912 " Exit BUILD_PALETTE:");
8915 if (mng_info->write_png8 == MagickFalse)
8918 /* Make any reductions necessary for the PNG8 format */
8919 if (image_colors <= 256 &&
8920 image_colors != 0 && image->colormap != NULL &&
8921 number_semitransparent == 0 &&
8922 number_transparent <= 1)
8925 /* PNG8 can't have semitransparent colors so we threshold the
8926 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8927 * transparent color so if more than one is transparent we merge
8928 * them into image->background_color.
8930 if (number_semitransparent != 0 || number_transparent > 1)
8932 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8933 " Thresholding the alpha channel to binary");
8935 for (y=0; y < (ssize_t) image->rows; y++)
8937 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8939 if (r == (Quantum *) NULL)
8942 for (x=0; x < (ssize_t) image->columns; x++)
8944 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8946 SetPixelInfoPixel(image,&image->background_color,r);
8947 SetPixelAlpha(image,TransparentAlpha,r);
8950 SetPixelAlpha(image,OpaqueAlpha,r);
8951 r+=GetPixelChannels(image);
8954 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8957 if (image_colors != 0 && image_colors <= 256 &&
8958 image->colormap != NULL)
8959 for (i=0; i<image_colors; i++)
8960 image->colormap[i].alpha =
8961 (image->colormap[i].alpha > TransparentAlpha/2 ?
8962 TransparentAlpha : OpaqueAlpha);
8967 /* PNG8 can't have more than 256 colors so we quantize the pixels and
8968 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
8969 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
8972 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
8974 if (logging != MagickFalse)
8975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8976 " Quantizing the background color to 4-4-4");
8978 tried_444 = MagickTrue;
8980 LBR04PacketRGB(image->background_color);
8982 if (logging != MagickFalse)
8983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8984 " Quantizing the pixel colors to 4-4-4");
8986 if (image->colormap == NULL)
8988 for (y=0; y < (ssize_t) image->rows; y++)
8990 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8992 if (r == (Quantum *) NULL)
8995 for (x=0; x < (ssize_t) image->columns; x++)
8997 if (GetPixelAlpha(image,r) == OpaqueAlpha)
8999 r+=GetPixelChannels(image);
9002 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9007 else /* Should not reach this; colormap already exists and
9010 if (logging != MagickFalse)
9011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9012 " Quantizing the colormap to 4-4-4");
9014 for (i=0; i<image_colors; i++)
9016 LBR04PacketRGB(image->colormap[i]);
9022 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9024 if (logging != MagickFalse)
9025 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026 " Quantizing the background color to 3-3-3");
9028 tried_333 = MagickTrue;
9030 LBR03PacketRGB(image->background_color);
9032 if (logging != MagickFalse)
9033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034 " Quantizing the pixel colors to 3-3-3-1");
9036 if (image->colormap == NULL)
9038 for (y=0; y < (ssize_t) image->rows; y++)
9040 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9042 if (r == (Quantum *) NULL)
9045 for (x=0; x < (ssize_t) image->columns; x++)
9047 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9049 r+=GetPixelChannels(image);
9052 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9057 else /* Should not reach this; colormap already exists and
9060 if (logging != MagickFalse)
9061 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9062 " Quantizing the colormap to 3-3-3-1");
9063 for (i=0; i<image_colors; i++)
9065 LBR03PacketRGB(image->colormap[i]);
9071 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9073 if (logging != MagickFalse)
9074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9075 " Quantizing the background color to 3-3-2");
9077 tried_332 = MagickTrue;
9079 /* Red and green were already done so we only quantize the blue
9083 LBR02PacketBlue(image->background_color);
9085 if (logging != MagickFalse)
9086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9087 " Quantizing the pixel colors to 3-3-2-1");
9089 if (image->colormap == NULL)
9091 for (y=0; y < (ssize_t) image->rows; y++)
9093 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9095 if (r == (Quantum *) NULL)
9098 for (x=0; x < (ssize_t) image->columns; x++)
9100 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9102 r+=GetPixelChannels(image);
9105 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9110 else /* Should not reach this; colormap already exists and
9113 if (logging != MagickFalse)
9114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115 " Quantizing the colormap to 3-3-2-1");
9116 for (i=0; i<image_colors; i++)
9118 LBR02PacketBlue(image->colormap[i]);
9124 if (image_colors == 0 || image_colors > 256)
9126 /* Take care of special case with 256 opaque colors + 1 transparent
9127 * color. We don't need to quantize to 2-3-2-1; we only need to
9128 * eliminate one color, so we'll merge the two darkest red
9129 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9131 if (logging != MagickFalse)
9132 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9133 " Merging two dark red background colors to 3-3-2-1");
9135 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9136 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9137 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9139 image->background_color.red=ScaleCharToQuantum(0x24);
9142 if (logging != MagickFalse)
9143 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9144 " Merging two dark red pixel colors to 3-3-2-1");
9146 if (image->colormap == NULL)
9148 for (y=0; y < (ssize_t) image->rows; y++)
9150 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9152 if (r == (Quantum *) NULL)
9155 for (x=0; x < (ssize_t) image->columns; x++)
9157 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9158 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9159 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9160 GetPixelAlpha(image,r) == OpaqueAlpha)
9162 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9164 r+=GetPixelChannels(image);
9167 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9175 for (i=0; i<image_colors; i++)
9177 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9178 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9179 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9181 image->colormap[i].red=ScaleCharToQuantum(0x24);
9188 /* END OF BUILD_PALETTE */
9190 /* If we are excluding the tRNS chunk and there is transparency,
9191 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9194 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9195 (number_transparent != 0 || number_semitransparent != 0))
9197 unsigned int colortype=mng_info->write_png_colortype;
9199 if (ping_have_color == MagickFalse)
9200 mng_info->write_png_colortype = 5;
9203 mng_info->write_png_colortype = 7;
9205 if (colortype != 0 &&
9206 mng_info->write_png_colortype != colortype)
9207 ping_need_colortype_warning=MagickTrue;
9211 /* See if cheap transparency is possible. It is only possible
9212 * when there is a single transparent color, no semitransparent
9213 * color, and no opaque color that has the same RGB components
9214 * as the transparent color. We only need this information if
9215 * we are writing a PNG with colortype 0 or 2, and we have not
9216 * excluded the tRNS chunk.
9218 if (number_transparent == 1 &&
9219 mng_info->write_png_colortype < 4)
9221 ping_have_cheap_transparency = MagickTrue;
9223 if (number_semitransparent != 0)
9224 ping_have_cheap_transparency = MagickFalse;
9226 else if (image_colors == 0 || image_colors > 256 ||
9227 image->colormap == NULL)
9229 register const Quantum
9232 for (y=0; y < (ssize_t) image->rows; y++)
9234 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9236 if (q == (Quantum *) NULL)
9239 for (x=0; x < (ssize_t) image->columns; x++)
9241 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9242 (unsigned short) GetPixelRed(image,q) ==
9243 ping_trans_color.red &&
9244 (unsigned short) GetPixelGreen(image,q) ==
9245 ping_trans_color.green &&
9246 (unsigned short) GetPixelBlue(image,q) ==
9247 ping_trans_color.blue)
9249 ping_have_cheap_transparency = MagickFalse;
9253 q+=GetPixelChannels(image);
9256 if (ping_have_cheap_transparency == MagickFalse)
9262 /* Assuming that image->colormap[0] is the one transparent color
9263 * and that all others are opaque.
9265 if (image_colors > 1)
9266 for (i=1; i<image_colors; i++)
9267 if (image->colormap[i].red == image->colormap[0].red &&
9268 image->colormap[i].green == image->colormap[0].green &&
9269 image->colormap[i].blue == image->colormap[0].blue)
9271 ping_have_cheap_transparency = MagickFalse;
9276 if (logging != MagickFalse)
9278 if (ping_have_cheap_transparency == MagickFalse)
9279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9280 " Cheap transparency is not possible.");
9283 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9284 " Cheap transparency is possible.");
9288 ping_have_cheap_transparency = MagickFalse;
9290 image_depth=image->depth;
9292 quantum_info = (QuantumInfo *) NULL;
9294 image_colors=(int) image->colors;
9295 image_matte=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse;
9297 if (mng_info->write_png_colortype < 5)
9298 mng_info->IsPalette=image->storage_class == PseudoClass &&
9299 image_colors <= 256 && image->colormap != NULL;
9301 mng_info->IsPalette = MagickFalse;
9303 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9304 (image->colors == 0 || image->colormap == NULL))
9306 image_info=DestroyImageInfo(image_info);
9307 image=DestroyImage(image);
9308 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9309 "Cannot write PNG8 or color-type 3; colormap is NULL",
9310 "`%s'",IMimage->filename);
9311 return(MagickFalse);
9315 Allocate the PNG structures
9317 #ifdef PNG_USER_MEM_SUPPORTED
9318 error_info.image=image;
9319 error_info.exception=exception;
9320 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9321 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9322 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9325 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9326 MagickPNGErrorHandler,MagickPNGWarningHandler);
9329 if (ping == (png_struct *) NULL)
9330 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9332 ping_info=png_create_info_struct(ping);
9334 if (ping_info == (png_info *) NULL)
9336 png_destroy_write_struct(&ping,(png_info **) NULL);
9337 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9340 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9341 pixel_info=(MemoryInfo *) NULL;
9343 if (setjmp(png_jmpbuf(ping)))
9349 if (image_info->verbose)
9350 (void) printf("PNG write has failed.\n");
9352 png_destroy_write_struct(&ping,&ping_info);
9353 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9354 UnlockSemaphoreInfo(ping_semaphore);
9357 if (pixel_info != (MemoryInfo *) NULL)
9358 pixel_info=RelinquishVirtualMemory(pixel_info);
9360 if (quantum_info != (QuantumInfo *) NULL)
9361 quantum_info=DestroyQuantumInfo(quantum_info);
9363 if (ping_have_blob != MagickFalse)
9364 (void) CloseBlob(image);
9365 image_info=DestroyImageInfo(image_info);
9366 image=DestroyImage(image);
9367 return(MagickFalse);
9370 /* { For navigation to end of SETJMP-protected block. Within this
9371 * block, use png_error() instead of Throwing an Exception, to ensure
9372 * that libpng is able to clean up, and that the semaphore is unlocked.
9375 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9376 LockSemaphoreInfo(ping_semaphore);
9379 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9380 /* Allow benign errors */
9381 png_set_benign_errors(ping, 1);
9385 Prepare PNG for writing.
9388 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9389 if (mng_info->write_mng)
9391 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9392 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9393 /* Disable new libpng-1.5.10 feature when writing a MNG because
9394 * zero-length PLTE is OK
9396 png_set_check_for_invalid_index (ping, 0);
9401 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9402 if (mng_info->write_mng)
9403 png_permit_empty_plte(ping,MagickTrue);
9410 ping_width=(png_uint_32) image->columns;
9411 ping_height=(png_uint_32) image->rows;
9413 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9416 if (mng_info->write_png48 || mng_info->write_png64)
9419 if (mng_info->write_png_depth != 0)
9420 image_depth=mng_info->write_png_depth;
9422 /* Adjust requested depth to next higher valid depth if necessary */
9423 if (image_depth > 8)
9426 if ((image_depth > 4) && (image_depth < 8))
9429 if (image_depth == 3)
9432 if (logging != MagickFalse)
9434 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435 " width=%.20g",(double) ping_width);
9436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9437 " height=%.20g",(double) ping_height);
9438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9439 " image_matte=%.20g",(double) image->alpha_trait);
9440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9441 " image->depth=%.20g",(double) image->depth);
9442 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9443 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9446 save_image_depth=image_depth;
9447 ping_bit_depth=(png_byte) save_image_depth;
9450 #if defined(PNG_pHYs_SUPPORTED)
9451 if (ping_exclude_pHYs == MagickFalse)
9453 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9454 (!mng_info->write_mng || !mng_info->equal_physs))
9456 if (logging != MagickFalse)
9457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9458 " Setting up pHYs chunk");
9460 if (image->units == PixelsPerInchResolution)
9462 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9463 ping_pHYs_x_resolution=
9464 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9465 ping_pHYs_y_resolution=
9466 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9469 else if (image->units == PixelsPerCentimeterResolution)
9471 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9472 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9473 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9478 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9479 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9480 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9483 if (logging != MagickFalse)
9484 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9485 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9486 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9487 (int) ping_pHYs_unit_type);
9488 ping_have_pHYs = MagickTrue;
9493 if (ping_exclude_bKGD == MagickFalse)
9495 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9501 if (ping_bit_depth == 8)
9504 if (ping_bit_depth == 4)
9507 if (ping_bit_depth == 2)
9510 if (ping_bit_depth == 1)
9513 ping_background.red=(png_uint_16)
9514 (ScaleQuantumToShort(image->background_color.red) & mask);
9516 ping_background.green=(png_uint_16)
9517 (ScaleQuantumToShort(image->background_color.green) & mask);
9519 ping_background.blue=(png_uint_16)
9520 (ScaleQuantumToShort(image->background_color.blue) & mask);
9522 ping_background.gray=(png_uint_16) ping_background.green;
9525 if (logging != MagickFalse)
9527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9528 " Setting up bKGD chunk (1)");
9529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9530 " background_color index is %d",
9531 (int) ping_background.index);
9533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9534 " ping_bit_depth=%d",ping_bit_depth);
9537 ping_have_bKGD = MagickTrue;
9541 Select the color type.
9546 if (mng_info->IsPalette && mng_info->write_png8)
9548 /* To do: make this a function cause it's used twice, except
9549 for reducing the sample depth from 8. */
9551 number_colors=image_colors;
9553 ping_have_tRNS=MagickFalse;
9558 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9560 if (logging != MagickFalse)
9561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9562 " Setting up PLTE chunk with %d colors (%d)",
9563 number_colors, image_colors);
9565 for (i=0; i < (ssize_t) number_colors; i++)
9567 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9568 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9569 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9570 if (logging != MagickFalse)
9571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9572 #if MAGICKCORE_QUANTUM_DEPTH == 8
9573 " %3ld (%3d,%3d,%3d)",
9575 " %5ld (%5d,%5d,%5d)",
9577 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9581 ping_have_PLTE=MagickTrue;
9582 image_depth=ping_bit_depth;
9585 if (matte != MagickFalse)
9588 Identify which colormap entry is transparent.
9590 assert(number_colors <= 256);
9591 assert(image->colormap != NULL);
9593 for (i=0; i < (ssize_t) number_transparent; i++)
9594 ping_trans_alpha[i]=0;
9597 ping_num_trans=(unsigned short) (number_transparent +
9598 number_semitransparent);
9600 if (ping_num_trans == 0)
9601 ping_have_tRNS=MagickFalse;
9604 ping_have_tRNS=MagickTrue;
9607 if (ping_exclude_bKGD == MagickFalse)
9610 * Identify which colormap entry is the background color.
9613 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9614 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9617 ping_background.index=(png_byte) i;
9619 if (logging != MagickFalse)
9621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9622 " background_color index is %d",
9623 (int) ping_background.index);
9626 } /* end of write_png8 */
9628 else if (mng_info->write_png_colortype == 1)
9630 image_matte=MagickFalse;
9631 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9634 else if (mng_info->write_png24 || mng_info->write_png48 ||
9635 mng_info->write_png_colortype == 3)
9637 image_matte=MagickFalse;
9638 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9641 else if (mng_info->write_png32 || mng_info->write_png64 ||
9642 mng_info->write_png_colortype == 7)
9644 image_matte=MagickTrue;
9645 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9648 else /* mng_info->write_pngNN not specified */
9650 image_depth=ping_bit_depth;
9652 if (mng_info->write_png_colortype != 0)
9654 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9656 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9657 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9658 image_matte=MagickTrue;
9661 image_matte=MagickFalse;
9663 if (logging != MagickFalse)
9664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9665 " PNG colortype %d was specified:",(int) ping_color_type);
9668 else /* write_png_colortype not specified */
9670 if (logging != MagickFalse)
9671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9672 " Selecting PNG colortype:");
9674 ping_color_type=(png_byte) ((matte != MagickFalse)?
9675 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9677 if (image_info->type == TrueColorType)
9679 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9680 image_matte=MagickFalse;
9683 if (image_info->type == TrueColorMatteType)
9685 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9686 image_matte=MagickTrue;
9689 if (image_info->type == PaletteType ||
9690 image_info->type == PaletteMatteType)
9691 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9693 if (mng_info->write_png_colortype == 0 &&
9694 image_info->type == UndefinedType)
9696 if (ping_have_color == MagickFalse)
9698 if (image_matte == MagickFalse)
9700 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9701 image_matte=MagickFalse;
9706 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9707 image_matte=MagickTrue;
9712 if (image_matte == MagickFalse)
9714 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9715 image_matte=MagickFalse;
9720 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9721 image_matte=MagickTrue;
9728 if (logging != MagickFalse)
9729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9730 " Selected PNG colortype=%d",ping_color_type);
9732 if (ping_bit_depth < 8)
9734 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9735 ping_color_type == PNG_COLOR_TYPE_RGB ||
9736 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9740 old_bit_depth=ping_bit_depth;
9742 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9744 if (image->alpha_trait == UndefinedPixelTrait && ping_have_non_bw == MagickFalse)
9748 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9753 if (image->colors == 0)
9756 png_error(ping,"image has 0 colors");
9759 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9760 ping_bit_depth <<= 1;
9763 if (logging != MagickFalse)
9765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9766 " Number of colors: %.20g",(double) image_colors);
9768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9769 " Tentative PNG bit depth: %d",ping_bit_depth);
9772 if (ping_bit_depth < (int) mng_info->write_png_depth)
9773 ping_bit_depth = mng_info->write_png_depth;
9776 image_depth=ping_bit_depth;
9778 if (logging != MagickFalse)
9780 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9781 " Tentative PNG color type: %s (%.20g)",
9782 PngColorTypeToString(ping_color_type),
9783 (double) ping_color_type);
9785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9786 " image_info->type: %.20g",(double) image_info->type);
9788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9789 " image_depth: %.20g",(double) image_depth);
9791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9793 " image->depth: %.20g",(double) image->depth);
9795 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9796 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9799 if (matte != MagickFalse)
9801 if (mng_info->IsPalette)
9803 if (mng_info->write_png_colortype == 0)
9805 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9807 if (ping_have_color != MagickFalse)
9808 ping_color_type=PNG_COLOR_TYPE_RGBA;
9812 * Determine if there is any transparent color.
9814 if (number_transparent + number_semitransparent == 0)
9817 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9820 image_matte=MagickFalse;
9822 if (mng_info->write_png_colortype == 0)
9823 ping_color_type&=0x03;
9833 if (ping_bit_depth == 8)
9836 if (ping_bit_depth == 4)
9839 if (ping_bit_depth == 2)
9842 if (ping_bit_depth == 1)
9845 ping_trans_color.red=(png_uint_16)
9846 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9848 ping_trans_color.green=(png_uint_16)
9849 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9851 ping_trans_color.blue=(png_uint_16)
9852 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9854 ping_trans_color.gray=(png_uint_16)
9855 (ScaleQuantumToShort(GetPixelInfoIntensity(
9856 image->colormap)) & mask);
9858 ping_trans_color.index=(png_byte) 0;
9860 ping_have_tRNS=MagickTrue;
9863 if (ping_have_tRNS != MagickFalse)
9866 * Determine if there is one and only one transparent color
9867 * and if so if it is fully transparent.
9869 if (ping_have_cheap_transparency == MagickFalse)
9870 ping_have_tRNS=MagickFalse;
9873 if (ping_have_tRNS != MagickFalse)
9875 if (mng_info->write_png_colortype == 0)
9876 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9878 if (image_depth == 8)
9880 ping_trans_color.red&=0xff;
9881 ping_trans_color.green&=0xff;
9882 ping_trans_color.blue&=0xff;
9883 ping_trans_color.gray&=0xff;
9889 if (image_depth == 8)
9891 ping_trans_color.red&=0xff;
9892 ping_trans_color.green&=0xff;
9893 ping_trans_color.blue&=0xff;
9894 ping_trans_color.gray&=0xff;
9901 if (ping_have_tRNS != MagickFalse)
9902 image_matte=MagickFalse;
9904 if ((mng_info->IsPalette) &&
9905 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9906 ping_have_color == MagickFalse &&
9907 (image_matte == MagickFalse || image_depth >= 8))
9911 if (image_matte != MagickFalse)
9912 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9914 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9916 ping_color_type=PNG_COLOR_TYPE_GRAY;
9918 if (save_image_depth == 16 && image_depth == 8)
9920 if (logging != MagickFalse)
9922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9923 " Scaling ping_trans_color (0)");
9925 ping_trans_color.gray*=0x0101;
9929 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9930 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9932 if ((image_colors == 0) ||
9933 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9934 image_colors=(int) (one << image_depth);
9936 if (image_depth > 8)
9942 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9944 if(!mng_info->write_png_depth)
9948 while ((int) (one << ping_bit_depth)
9949 < (ssize_t) image_colors)
9950 ping_bit_depth <<= 1;
9954 else if (ping_color_type ==
9955 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
9956 mng_info->IsPalette)
9958 /* Check if grayscale is reducible */
9961 depth_4_ok=MagickTrue,
9962 depth_2_ok=MagickTrue,
9963 depth_1_ok=MagickTrue;
9965 for (i=0; i < (ssize_t) image_colors; i++)
9970 intensity=ScaleQuantumToChar(image->colormap[i].red);
9972 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
9973 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
9974 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
9975 depth_2_ok=depth_1_ok=MagickFalse;
9976 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
9977 depth_1_ok=MagickFalse;
9980 if (depth_1_ok && mng_info->write_png_depth <= 1)
9983 else if (depth_2_ok && mng_info->write_png_depth <= 2)
9986 else if (depth_4_ok && mng_info->write_png_depth <= 4)
9991 image_depth=ping_bit_depth;
9996 if (mng_info->IsPalette)
9998 number_colors=image_colors;
10000 if (image_depth <= 8)
10005 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10007 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10009 for (i=0; i < (ssize_t) number_colors; i++)
10011 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10012 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10013 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10016 if (logging != MagickFalse)
10017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10018 " Setting up PLTE chunk with %d colors",
10021 ping_have_PLTE=MagickTrue;
10024 /* color_type is PNG_COLOR_TYPE_PALETTE */
10025 if (mng_info->write_png_depth == 0)
10033 while ((one << ping_bit_depth) < (size_t) number_colors)
10034 ping_bit_depth <<= 1;
10039 if (matte != MagickFalse)
10042 * Set up trans_colors array.
10044 assert(number_colors <= 256);
10046 ping_num_trans=(unsigned short) (number_transparent +
10047 number_semitransparent);
10049 if (ping_num_trans == 0)
10050 ping_have_tRNS=MagickFalse;
10054 if (logging != MagickFalse)
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057 " Scaling ping_trans_color (1)");
10059 ping_have_tRNS=MagickTrue;
10061 for (i=0; i < ping_num_trans; i++)
10063 ping_trans_alpha[i]= (png_byte)
10064 ScaleQuantumToChar(image->colormap[i].alpha);
10074 if (image_depth < 8)
10077 if ((save_image_depth == 16) && (image_depth == 8))
10079 if (logging != MagickFalse)
10081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10082 " Scaling ping_trans_color from (%d,%d,%d)",
10083 (int) ping_trans_color.red,
10084 (int) ping_trans_color.green,
10085 (int) ping_trans_color.blue);
10088 ping_trans_color.red*=0x0101;
10089 ping_trans_color.green*=0x0101;
10090 ping_trans_color.blue*=0x0101;
10091 ping_trans_color.gray*=0x0101;
10093 if (logging != MagickFalse)
10095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10097 (int) ping_trans_color.red,
10098 (int) ping_trans_color.green,
10099 (int) ping_trans_color.blue);
10104 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10105 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10108 Adjust background and transparency samples in sub-8-bit grayscale files.
10110 if (ping_bit_depth < 8 && ping_color_type ==
10111 PNG_COLOR_TYPE_GRAY)
10119 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10121 if (ping_exclude_bKGD == MagickFalse)
10124 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10125 (ScaleQuantumToShort(((GetPixelInfoIntensity(
10126 &image->background_color))) +.5)));
10128 if (logging != MagickFalse)
10129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10130 " Setting up bKGD chunk (2)");
10131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10132 " background_color index is %d",
10133 (int) ping_background.index);
10135 ping_have_bKGD = MagickTrue;
10138 if (logging != MagickFalse)
10139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10140 " Scaling ping_trans_color.gray from %d",
10141 (int)ping_trans_color.gray);
10143 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10144 ping_trans_color.gray)+.5);
10146 if (logging != MagickFalse)
10147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10148 " to %d", (int)ping_trans_color.gray);
10151 if (ping_exclude_bKGD == MagickFalse)
10153 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10156 Identify which colormap entry is the background color.
10159 number_colors=image_colors;
10161 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10162 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10165 ping_background.index=(png_byte) i;
10167 if (logging != MagickFalse)
10169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10170 " Setting up bKGD chunk with index=%d",(int) i);
10173 if (i < (ssize_t) number_colors)
10175 ping_have_bKGD = MagickTrue;
10177 if (logging != MagickFalse)
10179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10180 " background =(%d,%d,%d)",
10181 (int) ping_background.red,
10182 (int) ping_background.green,
10183 (int) ping_background.blue);
10187 else /* Can't happen */
10189 if (logging != MagickFalse)
10190 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10191 " No room in PLTE to add bKGD color");
10192 ping_have_bKGD = MagickFalse;
10197 if (logging != MagickFalse)
10198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10202 Initialize compression level and filtering.
10204 if (logging != MagickFalse)
10206 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10207 " Setting up deflate compression");
10209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10210 " Compression buffer size: 32768");
10213 png_set_compression_buffer_size(ping,32768L);
10215 if (logging != MagickFalse)
10216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10217 " Compression mem level: 9");
10219 png_set_compression_mem_level(ping, 9);
10221 /* Untangle the "-quality" setting:
10223 Undefined is 0; the default is used.
10228 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10229 zlib default compression level
10231 1-9: the zlib compression level
10235 0-4: the PNG filter method
10237 5: libpng adaptive filtering if compression level > 5
10238 libpng filter type "none" if compression level <= 5
10239 or if image is grayscale or palette
10241 6: libpng adaptive filtering
10243 7: "LOCO" filtering (intrapixel differing) if writing
10244 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10245 and earlier because of a missing "else".
10247 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10248 filtering. Unused prior to IM-6.7.0-10, was same as 6
10250 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10251 Unused prior to IM-6.7.0-10, was same as 6
10253 Note that using the -quality option, not all combinations of
10254 PNG filter type, zlib compression level, and zlib compression
10255 strategy are possible. This will be addressed soon in a
10256 release that accomodates "-define png:compression-strategy", etc.
10260 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10261 image_info->quality;
10265 if (mng_info->write_png_compression_strategy == 0)
10266 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10269 else if (mng_info->write_png_compression_level == 0)
10274 level=(int) MagickMin((ssize_t) quality/10,9);
10276 mng_info->write_png_compression_level = level+1;
10279 if (mng_info->write_png_compression_strategy == 0)
10281 if ((quality %10) == 8 || (quality %10) == 9)
10282 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10283 mng_info->write_png_compression_strategy=Z_RLE+1;
10285 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10289 if (mng_info->write_png_compression_filter == 0)
10290 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10292 if (logging != MagickFalse)
10294 if (mng_info->write_png_compression_level)
10295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10296 " Compression level: %d",
10297 (int) mng_info->write_png_compression_level-1);
10299 if (mng_info->write_png_compression_strategy)
10300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10301 " Compression strategy: %d",
10302 (int) mng_info->write_png_compression_strategy-1);
10304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10305 " Setting up filtering");
10307 if (mng_info->write_png_compression_filter == 6)
10308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10309 " Base filter method: ADAPTIVE");
10310 else if (mng_info->write_png_compression_filter == 0 ||
10311 mng_info->write_png_compression_filter == 1)
10312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10313 " Base filter method: NONE");
10315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10316 " Base filter method: %d",
10317 (int) mng_info->write_png_compression_filter-1);
10320 if (mng_info->write_png_compression_level != 0)
10321 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10323 if (mng_info->write_png_compression_filter == 6)
10325 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10326 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10328 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10330 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10332 else if (mng_info->write_png_compression_filter == 7 ||
10333 mng_info->write_png_compression_filter == 10)
10334 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10336 else if (mng_info->write_png_compression_filter == 8)
10338 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10339 if (mng_info->write_mng)
10341 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10342 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10343 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10346 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10349 else if (mng_info->write_png_compression_filter == 9)
10350 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10352 else if (mng_info->write_png_compression_filter != 0)
10353 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10354 mng_info->write_png_compression_filter-1);
10356 if (mng_info->write_png_compression_strategy != 0)
10357 png_set_compression_strategy(ping,
10358 mng_info->write_png_compression_strategy-1);
10360 ping_interlace_method=image_info->interlace != NoInterlace;
10362 if (mng_info->write_mng)
10363 png_set_sig_bytes(ping,8);
10365 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10367 if (mng_info->write_png_colortype != 0)
10369 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10370 if (ping_have_color != MagickFalse)
10372 ping_color_type = PNG_COLOR_TYPE_RGB;
10374 if (ping_bit_depth < 8)
10378 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10379 if (ping_have_color != MagickFalse)
10380 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10383 if (ping_need_colortype_warning != MagickFalse ||
10384 ((mng_info->write_png_depth &&
10385 (int) mng_info->write_png_depth != ping_bit_depth) ||
10386 (mng_info->write_png_colortype &&
10387 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10388 mng_info->write_png_colortype != 7 &&
10389 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10391 if (logging != MagickFalse)
10393 if (ping_need_colortype_warning != MagickFalse)
10395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10396 " Image has transparency but tRNS chunk was excluded");
10399 if (mng_info->write_png_depth)
10401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10402 " Defined png:bit-depth=%u, Computed depth=%u",
10403 mng_info->write_png_depth,
10407 if (mng_info->write_png_colortype)
10409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10410 " Defined png:color-type=%u, Computed color type=%u",
10411 mng_info->write_png_colortype-1,
10417 "Cannot write image with defined png:bit-depth or png:color-type.");
10420 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10422 /* Add an opaque matte channel */
10423 image->alpha_trait = BlendPixelTrait;
10424 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10426 if (logging != MagickFalse)
10427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10428 " Added an opaque matte channel");
10431 if (number_transparent != 0 || number_semitransparent != 0)
10433 if (ping_color_type < 4)
10435 ping_have_tRNS=MagickTrue;
10436 if (logging != MagickFalse)
10437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10438 " Setting ping_have_tRNS=MagickTrue.");
10442 if (logging != MagickFalse)
10443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10444 " Writing PNG header chunks");
10446 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10447 ping_bit_depth,ping_color_type,
10448 ping_interlace_method,ping_compression_method,
10449 ping_filter_method);
10451 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10453 png_set_PLTE(ping,ping_info,palette,number_colors);
10455 if (logging != MagickFalse)
10457 for (i=0; i< (ssize_t) number_colors; i++)
10459 if (i < ping_num_trans)
10460 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10461 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10463 (int) palette[i].red,
10464 (int) palette[i].green,
10465 (int) palette[i].blue,
10467 (int) ping_trans_alpha[i]);
10469 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10470 " PLTE[%d] = (%d,%d,%d)",
10472 (int) palette[i].red,
10473 (int) palette[i].green,
10474 (int) palette[i].blue);
10479 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10480 if (ping_exclude_sRGB != MagickFalse ||
10481 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10483 if ((ping_exclude_tEXt == MagickFalse ||
10484 ping_exclude_zTXt == MagickFalse) &&
10485 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10487 ResetImageProfileIterator(image);
10488 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10490 profile=GetImageProfile(image,name);
10492 if (profile != (StringInfo *) NULL)
10494 #ifdef PNG_WRITE_iCCP_SUPPORTED
10495 if ((LocaleCompare(name,"ICC") == 0) ||
10496 (LocaleCompare(name,"ICM") == 0))
10499 if (ping_exclude_iCCP == MagickFalse)
10501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10502 " Setting up iCCP chunk");
10504 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10505 #if (PNG_LIBPNG_VER < 10500)
10506 (png_charp) GetStringInfoDatum(profile),
10508 (png_const_bytep) GetStringInfoDatum(profile),
10510 (png_uint_32) GetStringInfoLength(profile));
10511 ping_have_iCCP = MagickTrue;
10517 if (ping_exclude_zCCP == MagickFalse)
10519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520 " Setting up zTXT chunk with uuencoded ICC");
10521 Magick_png_write_raw_profile(image_info,ping,ping_info,
10522 (unsigned char *) name,(unsigned char *) name,
10523 GetStringInfoDatum(profile),
10524 (png_uint_32) GetStringInfoLength(profile));
10525 ping_have_iCCP = MagickTrue;
10529 if (logging != MagickFalse)
10530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10531 " Setting up text chunk with %s profile",name);
10533 name=GetNextImageProfile(image);
10538 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10539 if ((mng_info->have_write_global_srgb == 0) &&
10540 ping_have_iCCP != MagickTrue &&
10541 (ping_have_sRGB != MagickFalse ||
10542 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10544 if (ping_exclude_sRGB == MagickFalse)
10547 Note image rendering intent.
10549 if (logging != MagickFalse)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " Setting up sRGB chunk");
10553 (void) png_set_sRGB(ping,ping_info,(
10554 Magick_RenderingIntent_to_PNG_RenderingIntent(
10555 image->rendering_intent)));
10557 ping_have_sRGB = MagickTrue;
10561 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10564 if (ping_exclude_gAMA == MagickFalse &&
10565 ping_have_iCCP == MagickFalse &&
10566 ping_have_sRGB == MagickFalse &&
10567 (ping_exclude_sRGB == MagickFalse ||
10568 (image->gamma < .45 || image->gamma > .46)))
10570 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10574 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10576 if (logging != MagickFalse)
10577 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10578 " Setting up gAMA chunk");
10580 png_set_gAMA(ping,ping_info,image->gamma);
10584 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10586 if ((mng_info->have_write_global_chrm == 0) &&
10587 (image->chromaticity.red_primary.x != 0.0))
10590 Note image chromaticity.
10591 Note: if cHRM+gAMA == sRGB write sRGB instead.
10599 wp=image->chromaticity.white_point;
10600 rp=image->chromaticity.red_primary;
10601 gp=image->chromaticity.green_primary;
10602 bp=image->chromaticity.blue_primary;
10604 if (logging != MagickFalse)
10605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10606 " Setting up cHRM chunk");
10608 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10614 if (ping_exclude_bKGD == MagickFalse)
10616 if (ping_have_bKGD != MagickFalse)
10618 png_set_bKGD(ping,ping_info,&ping_background);
10619 if (logging != MagickFalse)
10621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10622 " Setting up bKGD chunk");
10623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10624 " background color = (%d,%d,%d)",
10625 (int) ping_background.red,
10626 (int) ping_background.green,
10627 (int) ping_background.blue);
10628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10629 " index = %d, gray=%d",
10630 (int) ping_background.index,
10631 (int) ping_background.gray);
10636 if (ping_exclude_pHYs == MagickFalse)
10638 if (ping_have_pHYs != MagickFalse)
10640 png_set_pHYs(ping,ping_info,
10641 ping_pHYs_x_resolution,
10642 ping_pHYs_y_resolution,
10643 ping_pHYs_unit_type);
10645 if (logging != MagickFalse)
10647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648 " Setting up pHYs chunk");
10649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10650 " x_resolution=%lu",
10651 (unsigned long) ping_pHYs_x_resolution);
10652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10653 " y_resolution=%lu",
10654 (unsigned long) ping_pHYs_y_resolution);
10655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10657 (unsigned long) ping_pHYs_unit_type);
10662 #if defined(PNG_oFFs_SUPPORTED)
10663 if (ping_exclude_oFFs == MagickFalse)
10665 if (image->page.x || image->page.y)
10667 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10668 (png_int_32) image->page.y, 0);
10670 if (logging != MagickFalse)
10671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10672 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10673 (int) image->page.x, (int) image->page.y);
10678 #if defined(PNG_tIME_SUPPORTED)
10679 if (ping_exclude_tIME == MagickFalse)
10684 if (image->taint == MagickFalse)
10686 timestamp=GetImageOption(image_info,"png:tIME");
10688 if (timestamp == (const char *) NULL)
10689 timestamp=GetImageProperty(image,"png:tIME",exception);
10694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10695 " Reset tIME in tainted image");
10697 timestamp=GetImageProperty(image,"date:modify",exception);
10700 if (timestamp != (const char *) NULL)
10701 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10705 if (mng_info->need_blob != MagickFalse)
10707 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10709 png_error(ping,"WriteBlob Failed");
10711 ping_have_blob=MagickTrue;
10714 png_write_info_before_PLTE(ping, ping_info);
10716 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10718 if (logging != MagickFalse)
10720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10724 if (ping_color_type == 3)
10725 (void) png_set_tRNS(ping, ping_info,
10732 (void) png_set_tRNS(ping, ping_info,
10735 &ping_trans_color);
10737 if (logging != MagickFalse)
10739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10740 " tRNS color =(%d,%d,%d)",
10741 (int) ping_trans_color.red,
10742 (int) ping_trans_color.green,
10743 (int) ping_trans_color.blue);
10748 /* write any png-chunk-b profiles */
10749 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10751 png_write_info(ping,ping_info);
10753 /* write any PNG-chunk-m profiles */
10754 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10756 if (ping_exclude_vpAg == MagickFalse)
10758 if ((image->page.width != 0 && image->page.width != image->columns) ||
10759 (image->page.height != 0 && image->page.height != image->rows))
10764 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10765 PNGType(chunk,mng_vpAg);
10766 LogPNGChunk(logging,mng_vpAg,9L);
10767 PNGLong(chunk+4,(png_uint_32) image->page.width);
10768 PNGLong(chunk+8,(png_uint_32) image->page.height);
10769 chunk[12]=0; /* unit = pixels */
10770 (void) WriteBlob(image,13,chunk);
10771 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10775 #if (PNG_LIBPNG_VER == 10206)
10776 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10777 #define PNG_HAVE_IDAT 0x04
10778 ping->mode |= PNG_HAVE_IDAT;
10779 #undef PNG_HAVE_IDAT
10782 png_set_packing(ping);
10786 rowbytes=image->columns;
10787 if (image_depth > 8)
10789 switch (ping_color_type)
10791 case PNG_COLOR_TYPE_RGB:
10795 case PNG_COLOR_TYPE_GRAY_ALPHA:
10799 case PNG_COLOR_TYPE_RGBA:
10807 if (logging != MagickFalse)
10809 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10810 " Writing PNG image data");
10812 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10813 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10815 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10816 if (pixel_info == (MemoryInfo *) NULL)
10817 png_error(ping,"Allocation of memory for pixels failed");
10818 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10821 Initialize image scanlines.
10823 quantum_info=AcquireQuantumInfo(image_info,image);
10824 if (quantum_info == (QuantumInfo *) NULL)
10825 png_error(ping,"Memory allocation for quantum_info failed");
10826 quantum_info->format=UndefinedQuantumFormat;
10827 quantum_info->depth=image_depth;
10828 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10829 num_passes=png_set_interlace_handling(ping);
10831 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10832 !mng_info->write_png48 && !mng_info->write_png64 &&
10833 !mng_info->write_png32) &&
10834 (mng_info->IsPalette ||
10835 (image_info->type == BilevelType)) &&
10836 image_matte == MagickFalse &&
10837 ping_have_non_bw == MagickFalse)
10839 /* Palette, Bilevel, or Opaque Monochrome */
10840 register const Quantum
10843 quantum_info->depth=8;
10844 for (pass=0; pass < num_passes; pass++)
10847 Convert PseudoClass image to a PNG monochrome image.
10849 for (y=0; y < (ssize_t) image->rows; y++)
10851 if (logging != MagickFalse && y == 0)
10852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10853 " Writing row of pixels (0)");
10855 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10857 if (p == (const Quantum *) NULL)
10860 if (mng_info->IsPalette)
10862 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10863 quantum_info,GrayQuantum,ping_pixels,exception);
10864 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10865 mng_info->write_png_depth &&
10866 mng_info->write_png_depth != old_bit_depth)
10868 /* Undo pixel scaling */
10869 for (i=0; i < (ssize_t) image->columns; i++)
10870 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10871 >> (8-old_bit_depth));
10877 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10878 quantum_info,RedQuantum,ping_pixels,exception);
10881 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10882 for (i=0; i < (ssize_t) image->columns; i++)
10883 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10886 if (logging != MagickFalse && y == 0)
10887 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10888 " Writing row of pixels (1)");
10890 png_write_row(ping,ping_pixels);
10892 if (image->previous == (Image *) NULL)
10894 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10895 if (status == MagickFalse)
10901 else /* Not Palette, Bilevel, or Opaque Monochrome */
10903 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10904 !mng_info->write_png48 && !mng_info->write_png64 &&
10905 !mng_info->write_png32) && (image_matte != MagickFalse ||
10906 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10907 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10909 register const Quantum
10912 for (pass=0; pass < num_passes; pass++)
10915 for (y=0; y < (ssize_t) image->rows; y++)
10917 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10919 if (p == (const Quantum *) NULL)
10922 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10924 if (mng_info->IsPalette)
10925 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10926 quantum_info,GrayQuantum,ping_pixels,exception);
10929 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10930 quantum_info,RedQuantum,ping_pixels,exception);
10932 if (logging != MagickFalse && y == 0)
10933 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10934 " Writing GRAY PNG pixels (2)");
10937 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10939 if (logging != MagickFalse && y == 0)
10940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10941 " Writing GRAY_ALPHA PNG pixels (2)");
10943 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10944 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10947 if (logging != MagickFalse && y == 0)
10948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10949 " Writing row of pixels (2)");
10951 png_write_row(ping,ping_pixels);
10954 if (image->previous == (Image *) NULL)
10956 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
10957 if (status == MagickFalse)
10965 register const Quantum
10968 for (pass=0; pass < num_passes; pass++)
10970 if ((image_depth > 8) ||
10971 mng_info->write_png24 ||
10972 mng_info->write_png32 ||
10973 mng_info->write_png48 ||
10974 mng_info->write_png64 ||
10975 (!mng_info->write_png8 && !mng_info->IsPalette))
10977 for (y=0; y < (ssize_t) image->rows; y++)
10979 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
10981 if (p == (const Quantum *) NULL)
10984 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10986 if (image->storage_class == DirectClass)
10987 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10988 quantum_info,RedQuantum,ping_pixels,exception);
10991 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10992 quantum_info,GrayQuantum,ping_pixels,exception);
10995 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
10997 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10998 quantum_info,GrayAlphaQuantum,ping_pixels,
11001 if (logging != MagickFalse && y == 0)
11002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11003 " Writing GRAY_ALPHA PNG pixels (3)");
11006 else if (image_matte != MagickFalse)
11007 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11008 quantum_info,RGBAQuantum,ping_pixels,exception);
11011 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11012 quantum_info,RGBQuantum,ping_pixels,exception);
11014 if (logging != MagickFalse && y == 0)
11015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11016 " Writing row of pixels (3)");
11018 png_write_row(ping,ping_pixels);
11023 /* not ((image_depth > 8) ||
11024 mng_info->write_png24 || mng_info->write_png32 ||
11025 mng_info->write_png48 || mng_info->write_png64 ||
11026 (!mng_info->write_png8 && !mng_info->IsPalette))
11029 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11030 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11032 if (logging != MagickFalse)
11033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11034 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11036 quantum_info->depth=8;
11040 for (y=0; y < (ssize_t) image->rows; y++)
11042 if (logging != MagickFalse && y == 0)
11043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11044 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11046 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11048 if (p == (const Quantum *) NULL)
11051 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11053 quantum_info->depth=image->depth;
11055 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11056 quantum_info,GrayQuantum,ping_pixels,exception);
11059 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11061 if (logging != MagickFalse && y == 0)
11062 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063 " Writing GRAY_ALPHA PNG pixels (4)");
11065 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11066 quantum_info,GrayAlphaQuantum,ping_pixels,
11072 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11073 quantum_info,IndexQuantum,ping_pixels,exception);
11075 if (logging != MagickFalse && y <= 2)
11077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11078 " Writing row of non-gray pixels (4)");
11080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11081 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11082 (int)ping_pixels[0],(int)ping_pixels[1]);
11085 png_write_row(ping,ping_pixels);
11089 if (image->previous == (Image *) NULL)
11091 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
11092 if (status == MagickFalse)
11099 if (quantum_info != (QuantumInfo *) NULL)
11100 quantum_info=DestroyQuantumInfo(quantum_info);
11102 if (logging != MagickFalse)
11104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11105 " Wrote PNG image data");
11107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11108 " Width: %.20g",(double) ping_width);
11110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11111 " Height: %.20g",(double) ping_height);
11113 if (mng_info->write_png_depth)
11115 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11116 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11120 " PNG bit-depth written: %d",ping_bit_depth);
11122 if (mng_info->write_png_colortype)
11124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11125 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11129 " PNG color-type written: %d",ping_color_type);
11131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11132 " PNG Interlace method: %d",ping_interlace_method);
11135 Generate text chunks after IDAT.
11137 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11139 ResetImagePropertyIterator(image);
11140 property=GetNextImageProperty(image);
11141 while (property != (const char *) NULL)
11146 value=GetImageProperty(image,property,exception);
11148 /* Don't write any "png:" or "jpeg:" properties; those are just for
11149 * "identify" or for passing through to another JPEG
11151 if ((LocaleNCompare(property,"png:",4) != 0 &&
11152 LocaleNCompare(property,"jpeg:",5) != 0) &&
11155 /* Suppress density and units if we wrote a pHYs chunk */
11156 (ping_exclude_pHYs != MagickFalse ||
11157 LocaleCompare(property,"density") != 0 ||
11158 LocaleCompare(property,"units") != 0) &&
11160 /* Suppress the IM-generated Date:create and Date:modify */
11161 (ping_exclude_date == MagickFalse ||
11162 LocaleNCompare(property, "Date:",5) != 0))
11164 if (value != (const char *) NULL)
11167 #if PNG_LIBPNG_VER >= 10400
11168 text=(png_textp) png_malloc(ping,
11169 (png_alloc_size_t) sizeof(png_text));
11171 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11173 text[0].key=(char *) property;
11174 text[0].text=(char *) value;
11175 text[0].text_length=strlen(value);
11177 if (ping_exclude_tEXt != MagickFalse)
11178 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11180 else if (ping_exclude_zTXt != MagickFalse)
11181 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11185 text[0].compression=image_info->compression == NoCompression ||
11186 (image_info->compression == UndefinedCompression &&
11187 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11188 PNG_TEXT_COMPRESSION_zTXt ;
11191 if (logging != MagickFalse)
11193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11194 " Setting up text chunk");
11196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11197 " keyword: '%s'",text[0].key);
11200 png_set_text(ping,ping_info,text,1);
11201 png_free(ping,text);
11204 property=GetNextImageProperty(image);
11208 /* write any PNG-chunk-e profiles */
11209 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11211 if (logging != MagickFalse)
11212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11213 " Writing PNG end info");
11215 png_write_end(ping,ping_info);
11217 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11219 if (mng_info->page.x || mng_info->page.y ||
11220 (ping_width != mng_info->page.width) ||
11221 (ping_height != mng_info->page.height))
11227 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11229 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11230 PNGType(chunk,mng_FRAM);
11231 LogPNGChunk(logging,mng_FRAM,27L);
11233 chunk[5]=0; /* frame name separator (no name) */
11234 chunk[6]=1; /* flag for changing delay, for next frame only */
11235 chunk[7]=0; /* flag for changing frame timeout */
11236 chunk[8]=1; /* flag for changing frame clipping for next frame */
11237 chunk[9]=0; /* flag for changing frame sync_id */
11238 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11239 chunk[14]=0; /* clipping boundaries delta type */
11240 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11242 (png_uint_32) (mng_info->page.x + ping_width));
11243 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11245 (png_uint_32) (mng_info->page.y + ping_height));
11246 (void) WriteBlob(image,31,chunk);
11247 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11248 mng_info->old_framing_mode=4;
11249 mng_info->framing_mode=1;
11253 mng_info->framing_mode=3;
11255 if (mng_info->write_mng && !mng_info->need_fram &&
11256 ((int) image->dispose == 3))
11257 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11260 Free PNG resources.
11263 png_destroy_write_struct(&ping,&ping_info);
11265 pixel_info=RelinquishVirtualMemory(pixel_info);
11267 if (ping_have_blob != MagickFalse)
11268 (void) CloseBlob(image);
11270 image_info=DestroyImageInfo(image_info);
11271 image=DestroyImage(image);
11273 /* Store bit depth actually written */
11274 s[0]=(char) ping_bit_depth;
11277 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11279 if (logging != MagickFalse)
11280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11281 " exit WriteOnePNGImage()");
11283 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11284 UnlockSemaphoreInfo(ping_semaphore);
11287 /* } for navigation to beginning of SETJMP-protected block. Revert to
11288 * Throwing an Exception when an error occurs.
11291 return(MagickTrue);
11292 /* End write one PNG image */
11297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11301 % W r i t e P N G I m a g e %
11305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11307 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11308 % Multiple-image Network Graphics (MNG) image file.
11310 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11312 % The format of the WritePNGImage method is:
11314 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11315 % Image *image,ExceptionInfo *exception)
11317 % A description of each parameter follows:
11319 % o image_info: the image info.
11321 % o image: The image.
11323 % o exception: return any errors or warnings in this structure.
11325 % Returns MagickTrue on success, MagickFalse on failure.
11327 % Communicating with the PNG encoder:
11329 % While the datastream written is always in PNG format and normally would
11330 % be given the "png" file extension, this method also writes the following
11331 % pseudo-formats which are subsets of png:
11333 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11334 % a depth greater than 8, the depth is reduced. If transparency
11335 % is present, the tRNS chunk must only have values 0 and 255
11336 % (i.e., transparency is binary: fully opaque or fully
11337 % transparent). If other values are present they will be
11338 % 50%-thresholded to binary transparency. If more than 256
11339 % colors are present, they will be quantized to the 4-4-4-1,
11340 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11341 % of any resulting fully-transparent pixels is changed to
11342 % the image's background color.
11344 % If you want better quantization or dithering of the colors
11345 % or alpha than that, you need to do it before calling the
11346 % PNG encoder. The pixels contain 8-bit indices even if
11347 % they could be represented with 1, 2, or 4 bits. Grayscale
11348 % images will be written as indexed PNG files even though the
11349 % PNG grayscale type might be slightly more efficient. Please
11350 % note that writing to the PNG8 format may result in loss
11351 % of color and alpha data.
11353 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11354 % chunk can be present to convey binary transparency by naming
11355 % one of the colors as transparent. The only loss incurred
11356 % is reduction of sample depth to 8. If the image has more
11357 % than one transparent color, has semitransparent pixels, or
11358 % has an opaque pixel with the same RGB components as the
11359 % transparent color, an image is not written.
11361 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11362 % transparency is permitted, i.e., the alpha sample for
11363 % each pixel can have any value from 0 to 255. The alpha
11364 % channel is present even if the image is fully opaque.
11365 % The only loss in data is the reduction of the sample depth
11368 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11369 % chunk can be present to convey binary transparency by naming
11370 % one of the colors as transparent. If the image has more
11371 % than one transparent color, has semitransparent pixels, or
11372 % has an opaque pixel with the same RGB components as the
11373 % transparent color, an image is not written.
11375 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11376 % transparency is permitted, i.e., the alpha sample for
11377 % each pixel can have any value from 0 to 65535. The alpha
11378 % channel is present even if the image is fully opaque.
11380 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11381 % image, if the input was a PNG, is written. If these values
11382 % cannot be found, then "PNG00" falls back to the regular "PNG"
11385 % o -define: For more precise control of the PNG output, you can use the
11386 % Image options "png:bit-depth" and "png:color-type". These
11387 % can be set from the commandline with "-define" and also
11388 % from the application programming interfaces. The options
11389 % are case-independent and are converted to lowercase before
11390 % being passed to this encoder.
11392 % png:color-type can be 0, 2, 3, 4, or 6.
11394 % When png:color-type is 0 (Grayscale), png:bit-depth can
11395 % be 1, 2, 4, 8, or 16.
11397 % When png:color-type is 2 (RGB), png:bit-depth can
11400 % When png:color-type is 3 (Indexed), png:bit-depth can
11401 % be 1, 2, 4, or 8. This refers to the number of bits
11402 % used to store the index. The color samples always have
11403 % bit-depth 8 in indexed PNG files.
11405 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11406 % png:bit-depth can be 8 or 16.
11408 % If the image cannot be written without loss with the
11409 % requested bit-depth and color-type, a PNG file will not
11410 % be written, a warning will be issued, and the encoder will
11411 % return MagickFalse.
11413 % Since image encoders should not be responsible for the "heavy lifting",
11414 % the user should make sure that ImageMagick has already reduced the
11415 % image depth and number of colors and limit transparency to binary
11416 % transparency prior to attempting to write the image with depth, color,
11417 % or transparency limitations.
11419 % Note that another definition, "png:bit-depth-written" exists, but it
11420 % is not intended for external use. It is only used internally by the
11421 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11423 % It is possible to request that the PNG encoder write previously-formatted
11424 % ancillary chunks in the output PNG file, using the "-profile" commandline
11425 % option as shown below or by setting the profile via a programming
11428 % -profile PNG-chunk-x:<file>
11430 % where x is a location flag and <file> is a file containing the chunk
11431 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11432 % This encoder will compute the chunk length and CRC, so those must not
11433 % be included in the file.
11435 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11436 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11437 % of the same type, then add a short unique string after the "x" to prevent
11438 % subsequent profiles from overwriting the preceding ones, e.g.,
11440 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11442 % As of version 6.6.6 the following optimizations are always done:
11444 % o 32-bit depth is reduced to 16.
11445 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11446 % high byte and low byte are identical.
11447 % o Palette is sorted to remove unused entries and to put a
11448 % transparent color first, if BUILD_PNG_PALETTE is defined.
11449 % o Opaque matte channel is removed when writing an indexed PNG.
11450 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11451 % this can be done without loss and a larger bit depth N was not
11452 % requested via the "-define png:bit-depth=N" option.
11453 % o If matte channel is present but only one transparent color is
11454 % present, RGB+tRNS is written instead of RGBA
11455 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11456 % was requested when converting an opaque image).
11458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11460 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11461 Image *image,ExceptionInfo *exception)
11466 have_mng_structure,
11481 assert(image_info != (const ImageInfo *) NULL);
11482 assert(image_info->signature == MagickSignature);
11483 assert(image != (Image *) NULL);
11484 assert(image->signature == MagickSignature);
11485 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11486 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11488 Allocate a MngInfo structure.
11490 have_mng_structure=MagickFalse;
11491 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11493 if (mng_info == (MngInfo *) NULL)
11494 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11497 Initialize members of the MngInfo structure.
11499 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11500 mng_info->image=image;
11501 mng_info->equal_backgrounds=MagickTrue;
11502 have_mng_structure=MagickTrue;
11504 /* See if user has requested a specific PNG subformat */
11506 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11507 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11508 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11509 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11510 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11512 value=GetImageOption(image_info,"png:format");
11513 if (value == (char *) NULL)
11514 if (LocaleCompare(image_info->magick,"PNG00") == 0)
11516 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11519 " Format=%s",value);
11521 mng_info->write_png8 = MagickFalse;
11522 mng_info->write_png24 = MagickFalse;
11523 mng_info->write_png32 = MagickFalse;
11524 mng_info->write_png48 = MagickFalse;
11525 mng_info->write_png64 = MagickFalse;
11527 if (LocaleCompare(value,"png8") == 0)
11528 mng_info->write_png8 = MagickTrue;
11530 else if (LocaleCompare(value,"png24") == 0)
11531 mng_info->write_png24 = MagickTrue;
11533 else if (LocaleCompare(value,"png32") == 0)
11534 mng_info->write_png32 = MagickTrue;
11536 else if (LocaleCompare(value,"png48") == 0)
11537 mng_info->write_png48 = MagickTrue;
11539 else if (LocaleCompare(value,"png64") == 0)
11540 mng_info->write_png64 = MagickTrue;
11542 else if ((LocaleCompare(value,"png00") == 0) ||
11543 LocaleCompare(image_info->magick,"PNG00") == 0)
11545 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11546 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11548 if (value != (char *) NULL)
11550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11551 " png00 inherited bit depth=%s",value);
11553 if (LocaleCompare(value,"1") == 0)
11554 mng_info->write_png_depth = 1;
11556 else if (LocaleCompare(value,"2") == 0)
11557 mng_info->write_png_depth = 2;
11559 else if (LocaleCompare(value,"4") == 0)
11560 mng_info->write_png_depth = 4;
11562 else if (LocaleCompare(value,"8") == 0)
11563 mng_info->write_png_depth = 8;
11565 else if (LocaleCompare(value,"16") == 0)
11566 mng_info->write_png_depth = 16;
11569 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11571 if (value != (char *) NULL)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574 " png00 inherited color type=%s",value);
11576 if (LocaleCompare(value,"0") == 0)
11577 mng_info->write_png_colortype = 1;
11579 else if (LocaleCompare(value,"2") == 0)
11580 mng_info->write_png_colortype = 3;
11582 else if (LocaleCompare(value,"3") == 0)
11583 mng_info->write_png_colortype = 4;
11585 else if (LocaleCompare(value,"4") == 0)
11586 mng_info->write_png_colortype = 5;
11588 else if (LocaleCompare(value,"6") == 0)
11589 mng_info->write_png_colortype = 7;
11594 if (mng_info->write_png8)
11596 mng_info->write_png_colortype = /* 3 */ 4;
11597 mng_info->write_png_depth = 8;
11601 if (mng_info->write_png24)
11603 mng_info->write_png_colortype = /* 2 */ 3;
11604 mng_info->write_png_depth = 8;
11607 if (image->alpha_trait != UndefinedPixelTrait)
11608 (void) SetImageType(image,TrueColorMatteType,exception);
11611 (void) SetImageType(image,TrueColorType,exception);
11613 (void) SyncImage(image,exception);
11616 if (mng_info->write_png32)
11618 mng_info->write_png_colortype = /* 6 */ 7;
11619 mng_info->write_png_depth = 8;
11621 image->alpha_trait = BlendPixelTrait;
11623 (void) SetImageType(image,TrueColorMatteType,exception);
11624 (void) SyncImage(image,exception);
11627 if (mng_info->write_png48)
11629 mng_info->write_png_colortype = /* 2 */ 3;
11630 mng_info->write_png_depth = 16;
11633 if (image->alpha_trait != UndefinedPixelTrait)
11634 (void) SetImageType(image,TrueColorMatteType,exception);
11637 (void) SetImageType(image,TrueColorType,exception);
11639 (void) SyncImage(image,exception);
11642 if (mng_info->write_png64)
11644 mng_info->write_png_colortype = /* 6 */ 7;
11645 mng_info->write_png_depth = 16;
11647 image->alpha_trait = BlendPixelTrait;
11649 (void) SetImageType(image,TrueColorMatteType,exception);
11650 (void) SyncImage(image,exception);
11653 value=GetImageOption(image_info,"png:bit-depth");
11655 if (value != (char *) NULL)
11657 if (LocaleCompare(value,"1") == 0)
11658 mng_info->write_png_depth = 1;
11660 else if (LocaleCompare(value,"2") == 0)
11661 mng_info->write_png_depth = 2;
11663 else if (LocaleCompare(value,"4") == 0)
11664 mng_info->write_png_depth = 4;
11666 else if (LocaleCompare(value,"8") == 0)
11667 mng_info->write_png_depth = 8;
11669 else if (LocaleCompare(value,"16") == 0)
11670 mng_info->write_png_depth = 16;
11673 (void) ThrowMagickException(exception,
11674 GetMagickModule(),CoderWarning,
11675 "ignoring invalid defined png:bit-depth",
11678 if (logging != MagickFalse)
11679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11680 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11683 value=GetImageOption(image_info,"png:color-type");
11685 if (value != (char *) NULL)
11687 /* We must store colortype+1 because 0 is a valid colortype */
11688 if (LocaleCompare(value,"0") == 0)
11689 mng_info->write_png_colortype = 1;
11691 else if (LocaleCompare(value,"1") == 0)
11692 mng_info->write_png_colortype = 2;
11694 else if (LocaleCompare(value,"2") == 0)
11695 mng_info->write_png_colortype = 3;
11697 else if (LocaleCompare(value,"3") == 0)
11698 mng_info->write_png_colortype = 4;
11700 else if (LocaleCompare(value,"4") == 0)
11701 mng_info->write_png_colortype = 5;
11703 else if (LocaleCompare(value,"6") == 0)
11704 mng_info->write_png_colortype = 7;
11707 (void) ThrowMagickException(exception,
11708 GetMagickModule(),CoderWarning,
11709 "ignoring invalid defined png:color-type",
11712 if (logging != MagickFalse)
11713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11714 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11717 /* Check for chunks to be excluded:
11719 * The default is to not exclude any known chunks except for any
11720 * listed in the "unused_chunks" array, above.
11722 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11723 * define (in the image properties or in the image artifacts)
11724 * or via a mng_info member. For convenience, in addition
11725 * to or instead of a comma-separated list of chunks, the
11726 * "exclude-chunk" string can be simply "all" or "none".
11728 * The exclude-chunk define takes priority over the mng_info.
11730 * A "png:include-chunk" define takes priority over both the
11731 * mng_info and the "png:exclude-chunk" define. Like the
11732 * "exclude-chunk" string, it can define "all" or "none" as
11733 * well as a comma-separated list. Chunks that are unknown to
11734 * ImageMagick are always excluded, regardless of their "copy-safe"
11735 * status according to the PNG specification, and even if they
11736 * appear in the "include-chunk" list. Such defines appearing among
11737 * the image options take priority over those found among the image
11740 * Finally, all chunks listed in the "unused_chunks" array are
11741 * automatically excluded, regardless of the other instructions
11744 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11745 * will not be written and the gAMA chunk will only be written if it
11746 * is not between .45 and .46, or approximately (1.0/2.2).
11748 * If you exclude tRNS and the image has transparency, the colortype
11749 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11751 * The -strip option causes StripImage() to set the png:include-chunk
11752 * artifact to "none,trns,gama".
11755 mng_info->ping_exclude_bKGD=MagickFalse;
11756 mng_info->ping_exclude_cHRM=MagickFalse;
11757 mng_info->ping_exclude_date=MagickFalse;
11758 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11759 mng_info->ping_exclude_gAMA=MagickFalse;
11760 mng_info->ping_exclude_iCCP=MagickFalse;
11761 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11762 mng_info->ping_exclude_oFFs=MagickFalse;
11763 mng_info->ping_exclude_pHYs=MagickFalse;
11764 mng_info->ping_exclude_sRGB=MagickFalse;
11765 mng_info->ping_exclude_tEXt=MagickFalse;
11766 mng_info->ping_exclude_tIME=MagickFalse;
11767 mng_info->ping_exclude_tRNS=MagickFalse;
11768 mng_info->ping_exclude_vpAg=MagickFalse;
11769 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11770 mng_info->ping_exclude_zTXt=MagickFalse;
11772 mng_info->ping_preserve_colormap=MagickFalse;
11774 value=GetImageOption(image_info,"png:preserve-colormap");
11776 value=GetImageArtifact(image,"png:preserve-colormap");
11778 mng_info->ping_preserve_colormap=MagickTrue;
11780 mng_info->ping_preserve_iCCP=MagickFalse;
11782 value=GetImageOption(image_info,"png:preserve-iCCP");
11784 value=GetImageArtifact(image,"png:preserve-iCCP");
11786 mng_info->ping_preserve_iCCP=MagickTrue;
11788 /* These compression-level, compression-strategy, and compression-filter
11789 * defines take precedence over values from the -quality option.
11791 value=GetImageOption(image_info,"png:compression-level");
11793 value=GetImageArtifact(image,"png:compression-level");
11796 /* We have to add 1 to everything because 0 is a valid input,
11797 * and we want to use 0 (the default) to mean undefined.
11799 if (LocaleCompare(value,"0") == 0)
11800 mng_info->write_png_compression_level = 1;
11802 else if (LocaleCompare(value,"1") == 0)
11803 mng_info->write_png_compression_level = 2;
11805 else if (LocaleCompare(value,"2") == 0)
11806 mng_info->write_png_compression_level = 3;
11808 else if (LocaleCompare(value,"3") == 0)
11809 mng_info->write_png_compression_level = 4;
11811 else if (LocaleCompare(value,"4") == 0)
11812 mng_info->write_png_compression_level = 5;
11814 else if (LocaleCompare(value,"5") == 0)
11815 mng_info->write_png_compression_level = 6;
11817 else if (LocaleCompare(value,"6") == 0)
11818 mng_info->write_png_compression_level = 7;
11820 else if (LocaleCompare(value,"7") == 0)
11821 mng_info->write_png_compression_level = 8;
11823 else if (LocaleCompare(value,"8") == 0)
11824 mng_info->write_png_compression_level = 9;
11826 else if (LocaleCompare(value,"9") == 0)
11827 mng_info->write_png_compression_level = 10;
11830 (void) ThrowMagickException(exception,
11831 GetMagickModule(),CoderWarning,
11832 "ignoring invalid defined png:compression-level",
11836 value=GetImageOption(image_info,"png:compression-strategy");
11838 value=GetImageArtifact(image,"png:compression-strategy");
11841 if (LocaleCompare(value,"0") == 0)
11842 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11844 else if (LocaleCompare(value,"1") == 0)
11845 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11847 else if (LocaleCompare(value,"2") == 0)
11848 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11850 else if (LocaleCompare(value,"3") == 0)
11851 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11852 mng_info->write_png_compression_strategy = Z_RLE+1;
11854 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11857 else if (LocaleCompare(value,"4") == 0)
11858 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11859 mng_info->write_png_compression_strategy = Z_FIXED+1;
11861 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11865 (void) ThrowMagickException(exception,
11866 GetMagickModule(),CoderWarning,
11867 "ignoring invalid defined png:compression-strategy",
11871 value=GetImageOption(image_info,"png:compression-filter");
11873 value=GetImageArtifact(image,"png:compression-filter");
11876 /* To do: combinations of filters allowed by libpng
11877 * masks 0x08 through 0xf8
11879 * Implement this as a comma-separated list of 0,1,2,3,4,5
11880 * where 5 is a special case meaning PNG_ALL_FILTERS.
11883 if (LocaleCompare(value,"0") == 0)
11884 mng_info->write_png_compression_filter = 1;
11886 else if (LocaleCompare(value,"1") == 0)
11887 mng_info->write_png_compression_filter = 2;
11889 else if (LocaleCompare(value,"2") == 0)
11890 mng_info->write_png_compression_filter = 3;
11892 else if (LocaleCompare(value,"3") == 0)
11893 mng_info->write_png_compression_filter = 4;
11895 else if (LocaleCompare(value,"4") == 0)
11896 mng_info->write_png_compression_filter = 5;
11898 else if (LocaleCompare(value,"5") == 0)
11899 mng_info->write_png_compression_filter = 6;
11902 (void) ThrowMagickException(exception,
11903 GetMagickModule(),CoderWarning,
11904 "ignoring invalid defined png:compression-filter",
11908 for (source=0; source<8; source++)
11913 value=GetImageOption(image_info,"png:exclude-chunks");
11916 value=GetImageArtifact(image,"png:exclude-chunks");
11919 value=GetImageOption(image_info,"png:exclude-chunk");
11922 value=GetImageArtifact(image,"png:exclude-chunk");
11925 value=GetImageOption(image_info,"png:include-chunks");
11928 value=GetImageArtifact(image,"png:include-chunks");
11931 value=GetImageOption(image_info,"png:include-chunk");
11934 value=GetImageArtifact(image,"png:include-chunk");
11940 excluding = MagickTrue;
11942 excluding = MagickFalse;
11944 if (logging != MagickFalse)
11946 if (source == 0 || source == 2)
11947 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11948 " png:exclude-chunk=%s found in image options.\n", value);
11949 else if (source == 1 || source == 3)
11950 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11951 " png:exclude-chunk=%s found in image artifacts.\n", value);
11952 else if (source == 4 || source == 6)
11953 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11954 " png:include-chunk=%s found in image options.\n", value);
11955 else /* if (source == 5 || source == 7) */
11956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11957 " png:include-chunk=%s found in image artifacts.\n", value);
11960 if (IsOptionMember("all",value) != MagickFalse)
11962 mng_info->ping_exclude_bKGD=excluding;
11963 mng_info->ping_exclude_cHRM=excluding;
11964 mng_info->ping_exclude_date=excluding;
11965 mng_info->ping_exclude_EXIF=excluding;
11966 mng_info->ping_exclude_gAMA=excluding;
11967 mng_info->ping_exclude_iCCP=excluding;
11968 /* mng_info->ping_exclude_iTXt=excluding; */
11969 mng_info->ping_exclude_oFFs=excluding;
11970 mng_info->ping_exclude_pHYs=excluding;
11971 mng_info->ping_exclude_sRGB=excluding;
11972 mng_info->ping_exclude_tEXt=excluding;
11973 mng_info->ping_exclude_tIME=excluding;
11974 mng_info->ping_exclude_tRNS=excluding;
11975 mng_info->ping_exclude_vpAg=excluding;
11976 mng_info->ping_exclude_zCCP=excluding;
11977 mng_info->ping_exclude_zTXt=excluding;
11980 if (IsOptionMember("none",value) != MagickFalse)
11982 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
11984 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
11986 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
11988 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
11990 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
11992 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
11994 /* mng_info->ping_exclude_iTXt=!excluding; */
11995 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
11997 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
11999 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12001 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12003 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12005 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12007 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12009 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12011 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12015 if (IsOptionMember("bkgd",value) != MagickFalse)
12016 mng_info->ping_exclude_bKGD=excluding;
12018 if (IsOptionMember("chrm",value) != MagickFalse)
12019 mng_info->ping_exclude_cHRM=excluding;
12021 if (IsOptionMember("date",value) != MagickFalse)
12022 mng_info->ping_exclude_date=excluding;
12024 if (IsOptionMember("exif",value) != MagickFalse)
12025 mng_info->ping_exclude_EXIF=excluding;
12027 if (IsOptionMember("gama",value) != MagickFalse)
12028 mng_info->ping_exclude_gAMA=excluding;
12030 if (IsOptionMember("iccp",value) != MagickFalse)
12031 mng_info->ping_exclude_iCCP=excluding;
12034 if (IsOptionMember("itxt",value) != MagickFalse)
12035 mng_info->ping_exclude_iTXt=excluding;
12038 if (IsOptionMember("offs",value) != MagickFalse)
12039 mng_info->ping_exclude_oFFs=excluding;
12041 if (IsOptionMember("phys",value) != MagickFalse)
12042 mng_info->ping_exclude_pHYs=excluding;
12044 if (IsOptionMember("srgb",value) != MagickFalse)
12045 mng_info->ping_exclude_sRGB=excluding;
12047 if (IsOptionMember("text",value) != MagickFalse)
12048 mng_info->ping_exclude_tEXt=excluding;
12050 if (IsOptionMember("time",value) != MagickFalse)
12051 mng_info->ping_exclude_tIME=excluding;
12053 if (IsOptionMember("trns",value) != MagickFalse)
12054 mng_info->ping_exclude_tRNS=excluding;
12056 if (IsOptionMember("vpag",value) != MagickFalse)
12057 mng_info->ping_exclude_vpAg=excluding;
12059 if (IsOptionMember("zccp",value) != MagickFalse)
12060 mng_info->ping_exclude_zCCP=excluding;
12062 if (IsOptionMember("ztxt",value) != MagickFalse)
12063 mng_info->ping_exclude_zTXt=excluding;
12066 if (logging != MagickFalse)
12068 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12069 " Chunks to be excluded from the output png:");
12070 if (mng_info->ping_exclude_bKGD != MagickFalse)
12071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12073 if (mng_info->ping_exclude_cHRM != MagickFalse)
12074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12076 if (mng_info->ping_exclude_date != MagickFalse)
12077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12079 if (mng_info->ping_exclude_EXIF != MagickFalse)
12080 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12082 if (mng_info->ping_exclude_gAMA != MagickFalse)
12083 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12085 if (mng_info->ping_exclude_iCCP != MagickFalse)
12086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12089 if (mng_info->ping_exclude_iTXt != MagickFalse)
12090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12094 if (mng_info->ping_exclude_oFFs != MagickFalse)
12095 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12097 if (mng_info->ping_exclude_pHYs != MagickFalse)
12098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 if (mng_info->ping_exclude_sRGB != MagickFalse)
12101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12103 if (mng_info->ping_exclude_tEXt != MagickFalse)
12104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12106 if (mng_info->ping_exclude_tIME != MagickFalse)
12107 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12109 if (mng_info->ping_exclude_tRNS != MagickFalse)
12110 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12112 if (mng_info->ping_exclude_vpAg != MagickFalse)
12113 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12115 if (mng_info->ping_exclude_zCCP != MagickFalse)
12116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12118 if (mng_info->ping_exclude_zTXt != MagickFalse)
12119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12123 mng_info->need_blob = MagickTrue;
12125 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12127 MngInfoFreeStruct(mng_info,&have_mng_structure);
12129 if (logging != MagickFalse)
12130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12135 #if defined(JNG_SUPPORTED)
12137 /* Write one JNG image */
12138 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12139 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12160 jng_alpha_compression_method,
12161 jng_alpha_sample_depth,
12169 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12170 " Enter WriteOneJNGImage()");
12172 blob=(unsigned char *) NULL;
12173 jpeg_image=(Image *) NULL;
12174 jpeg_image_info=(ImageInfo *) NULL;
12177 transparent=image_info->type==GrayscaleMatteType ||
12178 image_info->type==TrueColorMatteType || image->alpha_trait != UndefinedPixelTrait;
12180 jng_alpha_sample_depth = 0;
12182 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12184 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12186 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12187 image_info->quality;
12189 if (jng_alpha_quality >= 1000)
12190 jng_alpha_quality /= 1000;
12194 if (transparent != 0)
12198 /* Create JPEG blob, image, and image_info */
12199 if (logging != MagickFalse)
12200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12201 " Creating jpeg_image_info for alpha.");
12203 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12205 if (jpeg_image_info == (ImageInfo *) NULL)
12206 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12208 if (logging != MagickFalse)
12209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12210 " Creating jpeg_image.");
12212 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12213 if (jpeg_image == (Image *) NULL)
12214 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12215 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12216 jpeg_image->alpha_trait=UndefinedPixelTrait;
12217 jpeg_image->quality=jng_alpha_quality;
12218 jpeg_image_info->type=GrayscaleType;
12219 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12220 (void) AcquireUniqueFilename(jpeg_image->filename);
12221 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,
12222 "%s",jpeg_image->filename);
12226 jng_alpha_compression_method=0;
12228 jng_alpha_sample_depth=0;
12231 /* To do: check bit depth of PNG alpha channel */
12233 /* Check if image is grayscale. */
12234 if (image_info->type != TrueColorMatteType && image_info->type !=
12235 TrueColorType && IsImageGray(image,exception))
12238 if (logging != MagickFalse)
12240 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12241 " JNG Quality = %d",(int) jng_quality);
12242 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12243 " JNG Color Type = %d",jng_color_type);
12244 if (transparent != 0)
12246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12247 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12249 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12251 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12255 if (transparent != 0)
12257 if (jng_alpha_compression_method==0)
12262 /* Encode alpha as a grayscale PNG blob */
12263 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12265 if (logging != MagickFalse)
12266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12267 " Creating PNG blob.");
12269 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MaxTextExtent);
12270 (void) CopyMagickString(jpeg_image->magick,"PNG",MaxTextExtent);
12271 jpeg_image_info->interlace=NoInterlace;
12273 /* Exclude all ancillary chunks */
12274 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12276 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12279 /* Retrieve sample depth used */
12280 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12281 if (value != (char *) NULL)
12282 jng_alpha_sample_depth= (unsigned int) value[0];
12286 /* Encode alpha as a grayscale JPEG blob */
12288 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12291 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MaxTextExtent);
12292 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12293 jpeg_image_info->interlace=NoInterlace;
12294 if (logging != MagickFalse)
12295 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12296 " Creating blob.");
12297 blob=ImageToBlob(jpeg_image_info,jpeg_image,&length,
12299 jng_alpha_sample_depth=8;
12301 if (logging != MagickFalse)
12302 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12303 " Successfully read jpeg_image into a blob, length=%.20g.",
12307 /* Destroy JPEG image and image_info */
12308 jpeg_image=DestroyImage(jpeg_image);
12309 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12310 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12313 /* Write JHDR chunk */
12314 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12315 PNGType(chunk,mng_JHDR);
12316 LogPNGChunk(logging,mng_JHDR,16L);
12317 PNGLong(chunk+4,(png_uint_32) image->columns);
12318 PNGLong(chunk+8,(png_uint_32) image->rows);
12319 chunk[12]=jng_color_type;
12320 chunk[13]=8; /* sample depth */
12321 chunk[14]=8; /*jng_image_compression_method */
12322 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12323 chunk[16]=jng_alpha_sample_depth;
12324 chunk[17]=jng_alpha_compression_method;
12325 chunk[18]=0; /*jng_alpha_filter_method */
12326 chunk[19]=0; /*jng_alpha_interlace_method */
12327 (void) WriteBlob(image,20,chunk);
12328 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12329 if (logging != MagickFalse)
12331 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12332 " JNG width:%15lu",(unsigned long) image->columns);
12334 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12335 " JNG height:%14lu",(unsigned long) image->rows);
12337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338 " JNG color type:%10d",jng_color_type);
12340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12341 " JNG sample depth:%8d",8);
12343 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12344 " JNG compression:%9d",8);
12346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12347 " JNG interlace:%11d",0);
12349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12350 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12353 " JNG alpha compression:%3d",jng_alpha_compression_method);
12355 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12356 " JNG alpha filter:%8d",0);
12358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12359 " JNG alpha interlace:%5d",0);
12362 /* Write any JNG-chunk-b profiles */
12363 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12366 Write leading ancillary chunks
12369 if (transparent != 0)
12372 Write JNG bKGD chunk
12383 if (jng_color_type == 8 || jng_color_type == 12)
12387 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12388 PNGType(chunk,mng_bKGD);
12389 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12390 red=ScaleQuantumToChar(image->background_color.red);
12391 green=ScaleQuantumToChar(image->background_color.green);
12392 blue=ScaleQuantumToChar(image->background_color.blue);
12399 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12400 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12403 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12406 Write JNG sRGB chunk
12408 (void) WriteBlobMSBULong(image,1L);
12409 PNGType(chunk,mng_sRGB);
12410 LogPNGChunk(logging,mng_sRGB,1L);
12412 if (image->rendering_intent != UndefinedIntent)
12413 chunk[4]=(unsigned char)
12414 Magick_RenderingIntent_to_PNG_RenderingIntent(
12415 (image->rendering_intent));
12418 chunk[4]=(unsigned char)
12419 Magick_RenderingIntent_to_PNG_RenderingIntent(
12420 (PerceptualIntent));
12422 (void) WriteBlob(image,5,chunk);
12423 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12427 if (image->gamma != 0.0)
12430 Write JNG gAMA chunk
12432 (void) WriteBlobMSBULong(image,4L);
12433 PNGType(chunk,mng_gAMA);
12434 LogPNGChunk(logging,mng_gAMA,4L);
12435 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12436 (void) WriteBlob(image,8,chunk);
12437 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12440 if ((mng_info->equal_chrms == MagickFalse) &&
12441 (image->chromaticity.red_primary.x != 0.0))
12447 Write JNG cHRM chunk
12449 (void) WriteBlobMSBULong(image,32L);
12450 PNGType(chunk,mng_cHRM);
12451 LogPNGChunk(logging,mng_cHRM,32L);
12452 primary=image->chromaticity.white_point;
12453 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12454 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12455 primary=image->chromaticity.red_primary;
12456 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12457 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12458 primary=image->chromaticity.green_primary;
12459 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12460 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12461 primary=image->chromaticity.blue_primary;
12462 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12463 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12464 (void) WriteBlob(image,36,chunk);
12465 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12469 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12472 Write JNG pHYs chunk
12474 (void) WriteBlobMSBULong(image,9L);
12475 PNGType(chunk,mng_pHYs);
12476 LogPNGChunk(logging,mng_pHYs,9L);
12477 if (image->units == PixelsPerInchResolution)
12479 PNGLong(chunk+4,(png_uint_32)
12480 (image->resolution.x*100.0/2.54+0.5));
12482 PNGLong(chunk+8,(png_uint_32)
12483 (image->resolution.y*100.0/2.54+0.5));
12490 if (image->units == PixelsPerCentimeterResolution)
12492 PNGLong(chunk+4,(png_uint_32)
12493 (image->resolution.x*100.0+0.5));
12495 PNGLong(chunk+8,(png_uint_32)
12496 (image->resolution.y*100.0+0.5));
12503 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12504 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12508 (void) WriteBlob(image,13,chunk);
12509 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12512 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12515 Write JNG oFFs chunk
12517 (void) WriteBlobMSBULong(image,9L);
12518 PNGType(chunk,mng_oFFs);
12519 LogPNGChunk(logging,mng_oFFs,9L);
12520 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12521 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12523 (void) WriteBlob(image,13,chunk);
12524 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12526 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12528 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12529 PNGType(chunk,mng_vpAg);
12530 LogPNGChunk(logging,mng_vpAg,9L);
12531 PNGLong(chunk+4,(png_uint_32) image->page.width);
12532 PNGLong(chunk+8,(png_uint_32) image->page.height);
12533 chunk[12]=0; /* unit = pixels */
12534 (void) WriteBlob(image,13,chunk);
12535 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12539 if (transparent != 0)
12541 if (jng_alpha_compression_method==0)
12549 /* Write IDAT chunk header */
12550 if (logging != MagickFalse)
12551 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12552 " Write IDAT chunks from blob, length=%.20g.",(double)
12555 /* Copy IDAT chunks */
12558 for (i=8; i<(ssize_t) length; i+=len+12)
12560 len=(size_t) (*p<<24)|((*(p+1))<<16)|((*(p+2))<<8)|(*(p+3));
12563 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12565 /* Found an IDAT chunk. */
12566 (void) WriteBlobMSBULong(image,len);
12567 LogPNGChunk(logging,mng_IDAT,len);
12568 (void) WriteBlob(image,len+4,p);
12569 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12574 if (logging != MagickFalse)
12575 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12576 " Skipping %c%c%c%c chunk, length=%.20g.",
12577 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12584 /* Write JDAA chunk header */
12585 if (logging != MagickFalse)
12586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12587 " Write JDAA chunk, length=%.20g.",(double) length);
12588 (void) WriteBlobMSBULong(image,(size_t) length);
12589 PNGType(chunk,mng_JDAA);
12590 LogPNGChunk(logging,mng_JDAA,length);
12591 /* Write JDAT chunk(s) data */
12592 (void) WriteBlob(image,4,chunk);
12593 (void) WriteBlob(image,length,blob);
12594 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12597 blob=(unsigned char *) RelinquishMagickMemory(blob);
12600 /* Encode image as a JPEG blob */
12601 if (logging != MagickFalse)
12602 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12603 " Creating jpeg_image_info.");
12604 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12605 if (jpeg_image_info == (ImageInfo *) NULL)
12606 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12608 if (logging != MagickFalse)
12609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12610 " Creating jpeg_image.");
12612 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12613 if (jpeg_image == (Image *) NULL)
12614 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12615 (void) CopyMagickString(jpeg_image->magick,"JPEG",MaxTextExtent);
12617 (void) AcquireUniqueFilename(jpeg_image->filename);
12618 (void) FormatLocaleString(jpeg_image_info->filename,MaxTextExtent,"%s",
12619 jpeg_image->filename);
12621 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12624 if (logging != MagickFalse)
12625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12626 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12627 (double) jpeg_image->rows);
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 (IsImageGray(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 */