2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % 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 const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
529 static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
530 static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
531 static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
532 static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
533 static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
534 static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
535 static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
536 static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
537 static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
538 static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
539 static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
540 static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
541 static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
542 static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
543 static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
544 static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
545 static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
546 static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
547 static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
548 static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
549 static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
550 static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
551 static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
552 static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
553 static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
554 static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
555 static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
556 static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
557 static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
558 static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
559 static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
560 static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
561 static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
563 #if defined(JNG_SUPPORTED)
564 static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
565 static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
566 static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
567 static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
568 static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
569 static const 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 const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
575 static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
576 static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
577 static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
578 static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
579 static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
580 static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
583 typedef struct _MngBox
592 typedef struct _MngPair
599 #ifdef MNG_OBJECT_BUFFERS
600 typedef struct _MngBuffer
632 typedef struct _MngInfo
635 #ifdef MNG_OBJECT_BUFFERS
637 *ob[MNG_MAX_OBJECTS];
648 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
649 bytes_in_read_buffer,
655 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
656 defined(PNG_MNG_FEATURES_SUPPORTED)
668 have_saved_bkgd_index,
669 have_write_global_chrm,
670 have_write_global_gama,
671 have_write_global_plte,
672 have_write_global_srgb,
686 x_off[MNG_MAX_OBJECTS],
687 y_off[MNG_MAX_OBJECTS];
693 object_clip[MNG_MAX_OBJECTS];
696 /* These flags could be combined into one byte */
697 exists[MNG_MAX_OBJECTS],
698 frozen[MNG_MAX_OBJECTS],
700 invisible[MNG_MAX_OBJECTS],
701 viewable[MNG_MAX_OBJECTS];
713 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
731 global_x_pixels_per_unit,
732 global_y_pixels_per_unit,
742 global_phys_unit_type,
757 write_png_compression_level,
758 write_png_compression_strategy,
759 write_png_compression_filter,
766 #ifdef MNG_BASI_SUPPORTED
774 basi_compression_method,
776 basi_interlace_method,
799 /* Added at version 6.6.6-7 */
807 /* ping_exclude_iTXt, */
814 ping_exclude_zCCP, /* hex-encoded iCCP */
816 ping_preserve_colormap,
817 /* Added at version 6.8.5-7 */
819 /* Added at version 6.8.9-9 */
826 Forward declarations.
828 static MagickBooleanType
829 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
831 static MagickBooleanType
832 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
834 #if defined(JNG_SUPPORTED)
835 static MagickBooleanType
836 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
839 #if PNG_LIBPNG_VER > 10011
842 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
843 static MagickBooleanType
844 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
846 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
848 * This is true if the high byte and the next highest byte of
849 * each sample of the image, the colormap, and the background color
850 * are equal to each other. We check this by seeing if the samples
851 * are unchanged when we scale them down to 8 and back up to Quantum.
853 * We don't use the method GetImageDepth() because it doesn't check
854 * background and doesn't handle PseudoClass specially.
857 #define QuantumToCharToQuantumEqQuantum(quantum) \
858 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
861 ok_to_reduce=MagickFalse;
863 if (image->depth >= 16)
870 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
871 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
872 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
873 MagickTrue : MagickFalse;
875 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
879 for (indx=0; indx < (ssize_t) image->colors; indx++)
882 QuantumToCharToQuantumEqQuantum(
883 image->colormap[indx].red) &&
884 QuantumToCharToQuantumEqQuantum(
885 image->colormap[indx].green) &&
886 QuantumToCharToQuantumEqQuantum(
887 image->colormap[indx].blue)) ?
888 MagickTrue : MagickFalse;
890 if (ok_to_reduce == MagickFalse)
895 if ((ok_to_reduce != MagickFalse) &&
896 (image->storage_class != PseudoClass))
904 for (y=0; y < (ssize_t) image->rows; y++)
906 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
908 if (p == (const Quantum *) NULL)
910 ok_to_reduce = MagickFalse;
914 for (x=(ssize_t) image->columns-1; x >= 0; x--)
917 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
918 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
919 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
920 MagickTrue : MagickFalse;
922 if (ok_to_reduce == MagickFalse)
925 p+=GetPixelChannels(image);
932 if (ok_to_reduce != MagickFalse)
934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
935 " OK to reduce PNG bit depth to 8 without loss of info");
939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
940 " Not OK to reduce PNG bit depth to 8 without loss of info");
946 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
948 static const char* PngColorTypeToString(const unsigned int color_type)
955 case PNG_COLOR_TYPE_GRAY:
958 case PNG_COLOR_TYPE_GRAY_ALPHA:
959 result = "Gray+Alpha";
961 case PNG_COLOR_TYPE_PALETTE:
964 case PNG_COLOR_TYPE_RGB:
967 case PNG_COLOR_TYPE_RGB_ALPHA:
968 result = "RGB+Alpha";
976 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
980 case PerceptualIntent:
986 case SaturationIntent:
997 static RenderingIntent
998 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1000 switch (ping_intent)
1003 return PerceptualIntent;
1006 return RelativeIntent;
1009 return SaturationIntent;
1012 return AbsoluteIntent;
1015 return UndefinedIntent;
1020 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1022 switch (ping_intent)
1025 return "Perceptual Intent";
1028 return "Relative Intent";
1031 return "Saturation Intent";
1034 return "Absolute Intent";
1037 return "Undefined Intent";
1042 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1044 switch (ping_colortype)
1062 return "UndefinedColorType";
1066 #endif /* PNG_LIBPNG_VER > 10011 */
1067 #endif /* MAGICKCORE_PNG_DELEGATE */
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1080 % IsMNG() returns MagickTrue if the image format type, identified by the
1081 % magick string, is MNG.
1083 % The format of the IsMNG method is:
1085 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1087 % A description of each parameter follows:
1089 % o magick: compare image format pattern against these bytes.
1091 % o length: Specifies the length of the magick string.
1095 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1098 return(MagickFalse);
1100 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1103 return(MagickFalse);
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1117 % IsJNG() returns MagickTrue if the image format type, identified by the
1118 % magick string, is JNG.
1120 % The format of the IsJNG method is:
1122 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1124 % A description of each parameter follows:
1126 % o magick: compare image format pattern against these bytes.
1128 % o length: Specifies the length of the magick string.
1132 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1135 return(MagickFalse);
1137 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1140 return(MagickFalse);
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1154 % IsPNG() returns MagickTrue if the image format type, identified by the
1155 % magick string, is PNG.
1157 % The format of the IsPNG method is:
1159 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1161 % A description of each parameter follows:
1163 % o magick: compare image format pattern against these bytes.
1165 % o length: Specifies the length of the magick string.
1168 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1171 return(MagickFalse);
1173 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1176 return(MagickFalse);
1179 #if defined(MAGICKCORE_PNG_DELEGATE)
1180 #if defined(__cplusplus) || defined(c_plusplus)
1184 #if (PNG_LIBPNG_VER > 10011)
1185 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1190 assert(image != (Image *) NULL);
1191 assert(image->signature == MagickCoreSignature);
1192 buffer[0]=(unsigned char) (value >> 24);
1193 buffer[1]=(unsigned char) (value >> 16);
1194 buffer[2]=(unsigned char) (value >> 8);
1195 buffer[3]=(unsigned char) value;
1196 return((size_t) WriteBlob(image,4,buffer));
1199 static void PNGLong(png_bytep p,png_uint_32 value)
1201 *p++=(png_byte) ((value >> 24) & 0xff);
1202 *p++=(png_byte) ((value >> 16) & 0xff);
1203 *p++=(png_byte) ((value >> 8) & 0xff);
1204 *p++=(png_byte) (value & 0xff);
1207 #if defined(JNG_SUPPORTED)
1208 static void PNGsLong(png_bytep p,png_int_32 value)
1210 *p++=(png_byte) ((value >> 24) & 0xff);
1211 *p++=(png_byte) ((value >> 16) & 0xff);
1212 *p++=(png_byte) ((value >> 8) & 0xff);
1213 *p++=(png_byte) (value & 0xff);
1217 static void PNGShort(png_bytep p,png_uint_16 value)
1219 *p++=(png_byte) ((value >> 8) & 0xff);
1220 *p++=(png_byte) (value & 0xff);
1223 static void PNGType(png_bytep p,const png_byte *type)
1225 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1228 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1231 if (logging != MagickFalse)
1232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1233 " Writing %c%c%c%c chunk, length: %.20g",
1234 type[0],type[1],type[2],type[3],(double) length);
1236 #endif /* PNG_LIBPNG_VER > 10011 */
1238 #if defined(__cplusplus) || defined(c_plusplus)
1242 #if PNG_LIBPNG_VER > 10011
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1248 % R e a d P N G I m a g e %
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1254 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1255 % Multiple-image Network Graphics (MNG) image file and returns it. It
1256 % allocates the memory necessary for the new Image structure and returns a
1257 % pointer to the new image or set of images.
1259 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1261 % The format of the ReadPNGImage method is:
1263 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1265 % A description of each parameter follows:
1267 % o image_info: the image info.
1269 % o exception: return any errors or warnings in this structure.
1271 % To do, more or less in chronological order (as of version 5.5.2,
1272 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1274 % Get 16-bit cheap transparency working.
1276 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1278 % Preserve all unknown and not-yet-handled known chunks found in input
1279 % PNG file and copy them into output PNG files according to the PNG
1282 % (At this point, PNG encoding should be in full MNG compliance)
1284 % Provide options for choice of background to use when the MNG BACK
1285 % chunk is not present or is not mandatory (i.e., leave transparent,
1286 % user specified, MNG BACK, PNG bKGD)
1288 % Implement LOOP/ENDL [done, but could do discretionary loops more
1289 % efficiently by linking in the duplicate frames.].
1291 % Decode and act on the MHDR simplicity profile (offer option to reject
1292 % files or attempt to process them anyway when the profile isn't LC or VLC).
1294 % Upgrade to full MNG without Delta-PNG.
1296 % o BACK [done a while ago except for background image ID]
1297 % o MOVE [done 15 May 1999]
1298 % o CLIP [done 15 May 1999]
1299 % o DISC [done 19 May 1999]
1300 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1301 % o SEEK [partially done 19 May 1999 (discard function only)]
1305 % o MNG-level tEXt/iTXt/zTXt
1310 % o iTXt (wait for libpng implementation).
1312 % Use the scene signature to discover when an identical scene is
1313 % being reused, and just point to the original image->exception instead
1314 % of storing another set of pixels. This not specific to MNG
1315 % but could be applied generally.
1317 % Upgrade to full MNG with Delta-PNG.
1319 % JNG tEXt/iTXt/zTXt
1321 % We will not attempt to read files containing the CgBI chunk.
1322 % They are really Xcode files meant for display on the iPhone.
1323 % These are not valid PNG files and it is impossible to recover
1324 % the original PNG from files that have been converted to Xcode-PNG,
1325 % since irretrievable loss of color data has occurred due to the
1326 % use of premultiplied alpha.
1329 #if defined(__cplusplus) || defined(c_plusplus)
1334 This the function that does the actual reading of data. It is
1335 the same as the one supplied in libpng, except that it receives the
1336 datastream from the ReadBlob() function instead of standard input.
1338 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1343 image=(Image *) png_get_io_ptr(png_ptr);
1349 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1350 if (check != length)
1353 msg[MagickPathExtent];
1355 (void) FormatLocaleString(msg,MagickPathExtent,
1356 "Expected %.20g bytes; found %.20g bytes",(double) length,
1358 png_warning(png_ptr,msg);
1359 png_error(png_ptr,"Read Exception");
1364 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1365 !defined(PNG_MNG_FEATURES_SUPPORTED)
1366 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1367 * older than libpng-1.0.3a, which was the first to allow the empty
1368 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1369 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1370 * encountered after an empty PLTE, so we have to look ahead for bKGD
1371 * chunks and remove them from the datastream that is passed to libpng,
1372 * and store their contents for later use.
1374 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1389 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1390 image=(Image *) mng_info->image;
1391 while (mng_info->bytes_in_read_buffer && length)
1393 data[i]=mng_info->read_buffer[i];
1394 mng_info->bytes_in_read_buffer--;
1400 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1402 if (check != length)
1403 png_error(png_ptr,"Read Exception");
1407 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1410 check=(png_size_t) ReadBlob(image,(size_t) length,
1411 (char *) mng_info->read_buffer);
1412 mng_info->read_buffer[4]=0;
1413 mng_info->bytes_in_read_buffer=4;
1414 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1415 mng_info->found_empty_plte=MagickTrue;
1416 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1418 mng_info->found_empty_plte=MagickFalse;
1419 mng_info->have_saved_bkgd_index=MagickFalse;
1423 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1426 check=(png_size_t) ReadBlob(image,(size_t) length,
1427 (char *) mng_info->read_buffer);
1428 mng_info->read_buffer[4]=0;
1429 mng_info->bytes_in_read_buffer=4;
1430 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1431 if (mng_info->found_empty_plte)
1434 Skip the bKGD data byte and CRC.
1437 ReadBlob(image,5,(char *) mng_info->read_buffer);
1438 check=(png_size_t) ReadBlob(image,(size_t) length,
1439 (char *) mng_info->read_buffer);
1440 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1441 mng_info->have_saved_bkgd_index=MagickTrue;
1442 mng_info->bytes_in_read_buffer=0;
1450 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1455 image=(Image *) png_get_io_ptr(png_ptr);
1461 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1463 if (check != length)
1464 png_error(png_ptr,"WriteBlob Failed");
1468 static void png_flush_data(png_structp png_ptr)
1473 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1474 static int PalettesAreEqual(Image *a,Image *b)
1479 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1480 return((int) MagickFalse);
1482 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1483 return((int) MagickFalse);
1485 if (a->colors != b->colors)
1486 return((int) MagickFalse);
1488 for (i=0; i < (ssize_t) a->colors; i++)
1490 if ((a->colormap[i].red != b->colormap[i].red) ||
1491 (a->colormap[i].green != b->colormap[i].green) ||
1492 (a->colormap[i].blue != b->colormap[i].blue))
1493 return((int) MagickFalse);
1496 return((int) MagickTrue);
1500 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1502 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1503 mng_info->exists[i] && !mng_info->frozen[i])
1505 #ifdef MNG_OBJECT_BUFFERS
1506 if (mng_info->ob[i] != (MngBuffer *) NULL)
1508 if (mng_info->ob[i]->reference_count > 0)
1509 mng_info->ob[i]->reference_count--;
1511 if (mng_info->ob[i]->reference_count == 0)
1513 if (mng_info->ob[i]->image != (Image *) NULL)
1514 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1516 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1519 mng_info->ob[i]=(MngBuffer *) NULL;
1521 mng_info->exists[i]=MagickFalse;
1522 mng_info->invisible[i]=MagickFalse;
1523 mng_info->viewable[i]=MagickFalse;
1524 mng_info->frozen[i]=MagickFalse;
1525 mng_info->x_off[i]=0;
1526 mng_info->y_off[i]=0;
1527 mng_info->object_clip[i].left=0;
1528 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1529 mng_info->object_clip[i].top=0;
1530 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1534 static void MngInfoFreeStruct(MngInfo *mng_info,
1535 MagickBooleanType *have_mng_structure)
1537 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1542 for (i=1; i < MNG_MAX_OBJECTS; i++)
1543 MngInfoDiscardObject(mng_info,i);
1545 if (mng_info->global_plte != (png_colorp) NULL)
1546 mng_info->global_plte=(png_colorp)
1547 RelinquishMagickMemory(mng_info->global_plte);
1549 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1550 *have_mng_structure=MagickFalse;
1554 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1560 if (box.left < box2.left)
1563 if (box.top < box2.top)
1566 if (box.right > box2.right)
1567 box.right=box2.right;
1569 if (box.bottom > box2.bottom)
1570 box.bottom=box2.bottom;
1575 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1581 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1583 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1584 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1585 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1586 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1587 if (delta_type != 0)
1589 box.left+=previous_box.left;
1590 box.right+=previous_box.right;
1591 box.top+=previous_box.top;
1592 box.bottom+=previous_box.bottom;
1598 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1604 Read two ssize_ts from CLON, MOVE or PAST chunk
1606 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1607 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1609 if (delta_type != 0)
1611 pair.a+=previous_pair.a;
1612 pair.b+=previous_pair.b;
1618 static long mng_get_long(unsigned char *p)
1620 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1623 typedef struct _PNGErrorInfo
1632 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1643 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1644 image=error_info->image;
1645 exception=error_info->exception;
1647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1648 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1650 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1651 "`%s'",image->filename);
1653 #if (PNG_LIBPNG_VER < 10500)
1654 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1655 * are building with libpng-1.4.x and can be ignored.
1657 longjmp(ping->jmpbuf,1);
1659 png_longjmp(ping,1);
1663 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1674 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1675 png_error(ping, message);
1677 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1678 image=error_info->image;
1679 exception=error_info->exception;
1680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1681 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1683 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1684 message,"`%s'",image->filename);
1687 #ifdef PNG_USER_MEM_SUPPORTED
1688 #if PNG_LIBPNG_VER >= 10400
1689 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1691 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1695 return((png_voidp) AcquireMagickMemory((size_t) size));
1699 Free a pointer. It is removed from the list at the same time.
1701 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1704 ptr=RelinquishMagickMemory(ptr);
1705 return((png_free_ptr) NULL);
1709 #if defined(__cplusplus) || defined(c_plusplus)
1714 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1715 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1720 register unsigned char
1734 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1735 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1736 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1737 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1738 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1742 /* look for newline */
1746 /* look for length */
1747 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1750 length=(png_uint_32) StringToLong(sp);
1752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1753 " length: %lu",(unsigned long) length);
1755 while (*sp != ' ' && *sp != '\n')
1758 /* allocate space */
1761 png_warning(ping,"invalid profile length");
1762 return(MagickFalse);
1765 profile=BlobToStringInfo((const void *) NULL,length);
1767 if (profile == (StringInfo *) NULL)
1769 png_warning(ping, "unable to copy profile");
1770 return(MagickFalse);
1773 /* copy profile, skipping white space and column 1 "=" signs */
1774 dp=GetStringInfoDatum(profile);
1777 for (i=0; i < (ssize_t) nibbles; i++)
1779 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1783 png_warning(ping, "ran out of profile data");
1784 profile=DestroyStringInfo(profile);
1785 return(MagickFalse);
1791 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1794 (*dp++)+=unhex[(int) *sp++];
1797 We have already read "Raw profile type.
1799 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1800 profile=DestroyStringInfo(profile);
1802 if (image_info->verbose)
1803 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1808 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1809 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1815 /* The unknown chunk structure contains the chunk data:
1820 Note that libpng has already taken care of the CRC handling.
1823 LogMagickEvent(CoderEvent,GetMagickModule(),
1824 " read_vpag_chunk: found %c%c%c%c chunk",
1825 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1827 if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1828 chunk->name[2] != 65 ||chunk-> name[3] != 103)
1829 return(0); /* Did not recognize */
1831 /* recognized vpAg */
1833 if (chunk->size != 9)
1834 return(-1); /* Error return */
1836 if (chunk->data[8] != 0)
1837 return(0); /* ImageMagick requires pixel units */
1839 image=(Image *) png_get_user_chunk_ptr(ping);
1841 image->page.width=(size_t) ((chunk->data[0] << 24) |
1842 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1844 image->page.height=(size_t) ((chunk->data[4] << 24) |
1845 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1847 /* Return one of the following: */
1848 /* return(-n); chunk had an error */
1849 /* return(0); did not recognize */
1850 /* return(n); success */
1857 #if defined(PNG_tIME_SUPPORTED)
1858 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1859 ExceptionInfo *exception)
1864 if (png_get_tIME(ping,info,&time))
1869 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1870 time->year,time->month,time->day,time->hour,time->minute,time->second);
1871 SetImageProperty(image,"png:tIME",timestamp,exception);
1877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1881 % R e a d O n e P N G I m a g e %
1885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1887 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1888 % (minus the 8-byte signature) and returns it. It allocates the memory
1889 % necessary for the new Image structure and returns a pointer to the new
1892 % The format of the ReadOnePNGImage method is:
1894 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1895 % ExceptionInfo *exception)
1897 % A description of each parameter follows:
1899 % o mng_info: Specifies a pointer to a MngInfo structure.
1901 % o image_info: the image info.
1903 % o exception: return any errors or warnings in this structure.
1906 static Image *ReadOnePNGImage(MngInfo *mng_info,
1907 const ImageInfo *image_info, ExceptionInfo *exception)
1909 /* Read one PNG image */
1911 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1924 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
1934 ping_interlace_method,
1935 ping_compression_method,
1949 ping_found_sRGB_cHRM,
1954 *volatile pixel_info;
1992 register unsigned char
2012 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2013 png_byte unused_chunks[]=
2015 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2016 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2017 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2018 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2019 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2020 #if !defined(PNG_tIME_SUPPORTED)
2021 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2023 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2024 /* ignore the APNG chunks */
2025 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2026 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2027 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2032 /* Define these outside of the following "if logging()" block so they will
2033 * show in debuggers.
2036 (void) ConcatenateMagickString(im_vers,
2037 MagickLibVersionText,32);
2038 (void) ConcatenateMagickString(im_vers,
2039 MagickLibAddendum,32);
2042 (void) ConcatenateMagickString(libpng_vers,
2043 PNG_LIBPNG_VER_STRING,32);
2045 (void) ConcatenateMagickString(libpng_runv,
2046 png_get_libpng_ver(NULL),32);
2049 (void) ConcatenateMagickString(zlib_vers,
2052 (void) ConcatenateMagickString(zlib_runv,
2055 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2056 " Enter ReadOnePNGImage()\n"
2057 " IM version = %s\n"
2058 " Libpng version = %s",
2059 im_vers, libpng_vers);
2061 if (logging != MagickFalse)
2063 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2065 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2068 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2070 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2072 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2077 #if (PNG_LIBPNG_VER < 10200)
2078 if (image_info->verbose)
2079 printf("Your PNG library (libpng-%s) is rather old.\n",
2080 PNG_LIBPNG_VER_STRING);
2083 #if (PNG_LIBPNG_VER >= 10400)
2084 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2085 if (image_info->verbose)
2087 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2088 PNG_LIBPNG_VER_STRING);
2089 printf("Please update it.\n");
2095 quantum_info = (QuantumInfo *) NULL;
2096 image=mng_info->image;
2098 if (logging != MagickFalse)
2100 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2101 " Before reading:\n"
2102 " image->alpha_trait=%d"
2103 " image->rendering_intent=%d\n"
2104 " image->colorspace=%d\n"
2106 (int) image->alpha_trait, (int) image->rendering_intent,
2107 (int) image->colorspace, image->gamma);
2109 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2111 /* Set to an out-of-range color unless tRNS chunk is present */
2112 transparent_color.red=65537;
2113 transparent_color.green=65537;
2114 transparent_color.blue=65537;
2115 transparent_color.alpha=65537;
2120 num_raw_profiles = 0;
2122 ping_found_cHRM = MagickFalse;
2123 ping_found_gAMA = MagickFalse;
2124 ping_found_iCCP = MagickFalse;
2125 ping_found_sRGB = MagickFalse;
2126 ping_found_sRGB_cHRM = MagickFalse;
2127 ping_preserve_iCCP = MagickFalse;
2131 Allocate the PNG structures
2133 #ifdef PNG_USER_MEM_SUPPORTED
2134 error_info.image=image;
2135 error_info.exception=exception;
2136 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2137 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2138 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2140 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2141 MagickPNGErrorHandler,MagickPNGWarningHandler);
2143 if (ping == (png_struct *) NULL)
2144 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2146 ping_info=png_create_info_struct(ping);
2148 if (ping_info == (png_info *) NULL)
2150 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2151 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2154 end_info=png_create_info_struct(ping);
2156 if (end_info == (png_info *) NULL)
2158 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2159 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2162 pixel_info=(MemoryInfo *) NULL;
2164 if (setjmp(png_jmpbuf(ping)))
2167 PNG image is corrupt.
2169 png_destroy_read_struct(&ping,&ping_info,&end_info);
2171 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2172 UnlockSemaphoreInfo(ping_semaphore);
2175 if (pixel_info != (MemoryInfo *) NULL)
2176 pixel_info=RelinquishVirtualMemory(pixel_info);
2178 if (logging != MagickFalse)
2179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180 " exit ReadOnePNGImage() with error.");
2182 return(GetFirstImageInList(image));
2185 /* { For navigation to end of SETJMP-protected block. Within this
2186 * block, use png_error() instead of Throwing an Exception, to ensure
2187 * that libpng is able to clean up, and that the semaphore is unlocked.
2190 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2191 LockSemaphoreInfo(ping_semaphore);
2194 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2195 /* Allow benign errors */
2196 png_set_benign_errors(ping, 1);
2199 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2200 /* Reject images with too many rows or columns */
2201 png_set_user_limits(ping,
2202 (png_uint_32) MagickMin(0x7fffffffL,
2203 GetMagickResourceLimit(WidthResource)),
2204 (png_uint_32) MagickMin(0x7fffffffL,
2205 GetMagickResourceLimit(HeightResource)));
2206 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2209 Prepare PNG for reading.
2212 mng_info->image_found++;
2213 png_set_sig_bytes(ping,8);
2215 if (LocaleCompare(image_info->magick,"MNG") == 0)
2217 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2218 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2219 png_set_read_fn(ping,image,png_get_data);
2221 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2222 png_permit_empty_plte(ping,MagickTrue);
2223 png_set_read_fn(ping,image,png_get_data);
2225 mng_info->image=image;
2226 mng_info->bytes_in_read_buffer=0;
2227 mng_info->found_empty_plte=MagickFalse;
2228 mng_info->have_saved_bkgd_index=MagickFalse;
2229 png_set_read_fn(ping,mng_info,mng_get_data);
2235 png_set_read_fn(ping,image,png_get_data);
2241 value=GetImageOption(image_info,"profile:skip");
2243 if (IsOptionMember("ICC",value) == MagickFalse)
2246 value=GetImageOption(image_info,"png:preserve-iCCP");
2249 value=GetImageArtifact(image,"png:preserve-iCCP");
2252 ping_preserve_iCCP=MagickTrue;
2254 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2255 /* Don't let libpng check for ICC/sRGB profile because we're going
2256 * to do that anyway. This feature was added at libpng-1.6.12.
2257 * If logging, go ahead and check and issue a warning as appropriate.
2259 if (logging == MagickFalse)
2260 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2263 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2266 png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2270 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2271 /* Ignore unused chunks and all unknown chunks except for vpAg */
2272 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2273 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2275 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2277 png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2278 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2279 (int)sizeof(unused_chunks)/5);
2280 /* Callback for other unknown chunks */
2281 png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2284 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2285 # if (PNG_LIBPNG_VER >= 10400)
2286 /* Limit the size of the chunk storage cache used for sPLT, text,
2287 * and unknown chunks.
2289 png_set_chunk_cache_max(ping, 32767);
2293 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2294 /* Disable new libpng-1.5.10 feature */
2295 png_set_check_for_invalid_index (ping, 0);
2298 #if (PNG_LIBPNG_VER < 10400)
2299 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2300 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2301 /* Disable thread-unsafe features of pnggccrd */
2302 if (png_access_version_number() >= 10200)
2304 png_uint_32 mmx_disable_mask=0;
2305 png_uint_32 asm_flags;
2307 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2308 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2309 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2310 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2311 asm_flags=png_get_asm_flags(ping);
2312 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2317 png_read_info(ping,ping_info);
2319 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2320 &ping_bit_depth,&ping_color_type,
2321 &ping_interlace_method,&ping_compression_method,
2322 &ping_filter_method);
2324 ping_file_depth = ping_bit_depth;
2326 /* Swap bytes if requested */
2327 if (ping_file_depth == 16)
2332 value=GetImageOption(image_info,"png:swap-bytes");
2335 value=GetImageArtifact(image,"png:swap-bytes");
2341 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2344 msg[MagickPathExtent];
2346 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_color_type);
2347 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2349 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_bit_depth);
2350 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2353 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2356 (void) png_get_bKGD(ping, ping_info, &ping_background);
2358 if (ping_bit_depth < 8)
2360 png_set_packing(ping);
2364 image->depth=ping_bit_depth;
2365 image->depth=GetImageQuantumDepth(image,MagickFalse);
2366 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2368 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2369 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2371 image->rendering_intent=UndefinedIntent;
2372 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2373 (void) ResetMagickMemory(&image->chromaticity,0,
2374 sizeof(image->chromaticity));
2377 if (logging != MagickFalse)
2379 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2380 " PNG width: %.20g, height: %.20g\n"
2381 " PNG color_type: %d, bit_depth: %d\n"
2382 " PNG compression_method: %d\n"
2383 " PNG interlace_method: %d, filter_method: %d",
2384 (double) ping_width, (double) ping_height,
2385 ping_color_type, ping_bit_depth,
2386 ping_compression_method,
2387 ping_interlace_method,ping_filter_method);
2391 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2393 ping_found_iCCP=MagickTrue;
2394 if (logging != MagickFalse)
2395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2396 " Found PNG iCCP chunk.");
2399 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2401 ping_found_gAMA=MagickTrue;
2402 if (logging != MagickFalse)
2403 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2404 " Found PNG gAMA chunk.");
2407 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2409 ping_found_cHRM=MagickTrue;
2410 if (logging != MagickFalse)
2411 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2412 " Found PNG cHRM chunk.");
2415 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2418 ping_found_sRGB=MagickTrue;
2419 if (logging != MagickFalse)
2420 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2421 " Found PNG sRGB chunk.");
2424 #ifdef PNG_READ_iCCP_SUPPORTED
2425 if (ping_found_iCCP !=MagickTrue &&
2426 ping_found_sRGB != MagickTrue &&
2427 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2429 ping_found_iCCP=MagickTrue;
2430 if (logging != MagickFalse)
2431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2432 " Found PNG iCCP chunk.");
2435 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2440 #if (PNG_LIBPNG_VER < 10500)
2454 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2457 if (profile_length != 0)
2462 if (logging != MagickFalse)
2463 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2464 " Reading PNG iCCP chunk.");
2466 profile=BlobToStringInfo(info,profile_length);
2468 if (profile == (StringInfo *) NULL)
2470 png_warning(ping, "ICC profile is NULL");
2471 profile=DestroyStringInfo(profile);
2475 if (ping_preserve_iCCP == MagickFalse)
2489 length=(png_uint_32) GetStringInfoLength(profile);
2491 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2493 if (length == sRGB_info[icheck].len)
2497 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2498 " Got a %lu-byte ICC profile (potentially sRGB)",
2499 (unsigned long) length);
2501 data=GetStringInfoDatum(profile);
2502 profile_crc=crc32(0,data,length);
2504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2505 " with crc=%8x",(unsigned int) profile_crc);
2509 if (profile_crc == sRGB_info[icheck].crc)
2511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512 " It is sRGB with rendering intent = %s",
2513 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2514 sRGB_info[icheck].intent));
2515 if (image->rendering_intent==UndefinedIntent)
2517 image->rendering_intent=
2518 Magick_RenderingIntent_from_PNG_RenderingIntent(
2519 sRGB_info[icheck].intent);
2525 if (sRGB_info[icheck].len == 0)
2527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2528 " Got a %lu-byte ICC profile not recognized as sRGB",
2529 (unsigned long) length);
2530 (void) SetImageProfile(image,"icc",profile,exception);
2533 else /* Preserve-iCCP */
2535 (void) SetImageProfile(image,"icc",profile,exception);
2538 profile=DestroyStringInfo(profile);
2544 #if defined(PNG_READ_sRGB_SUPPORTED)
2546 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2549 if (png_get_sRGB(ping,ping_info,&intent))
2551 if (image->rendering_intent == UndefinedIntent)
2552 image->rendering_intent=
2553 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2555 if (logging != MagickFalse)
2556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2557 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2561 else if (mng_info->have_global_srgb)
2563 if (image->rendering_intent == UndefinedIntent)
2564 image->rendering_intent=
2565 Magick_RenderingIntent_from_PNG_RenderingIntent
2566 (mng_info->global_srgb_intent);
2573 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2574 if (mng_info->have_global_gama)
2575 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2577 if (png_get_gAMA(ping,ping_info,&file_gamma))
2579 image->gamma=(float) file_gamma;
2580 if (logging != MagickFalse)
2581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2586 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2588 if (mng_info->have_global_chrm != MagickFalse)
2590 (void) png_set_cHRM(ping,ping_info,
2591 mng_info->global_chrm.white_point.x,
2592 mng_info->global_chrm.white_point.y,
2593 mng_info->global_chrm.red_primary.x,
2594 mng_info->global_chrm.red_primary.y,
2595 mng_info->global_chrm.green_primary.x,
2596 mng_info->global_chrm.green_primary.y,
2597 mng_info->global_chrm.blue_primary.x,
2598 mng_info->global_chrm.blue_primary.y);
2602 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2604 (void) png_get_cHRM(ping,ping_info,
2605 &image->chromaticity.white_point.x,
2606 &image->chromaticity.white_point.y,
2607 &image->chromaticity.red_primary.x,
2608 &image->chromaticity.red_primary.y,
2609 &image->chromaticity.green_primary.x,
2610 &image->chromaticity.green_primary.y,
2611 &image->chromaticity.blue_primary.x,
2612 &image->chromaticity.blue_primary.y);
2614 ping_found_cHRM=MagickTrue;
2616 if (image->chromaticity.red_primary.x>0.6399f &&
2617 image->chromaticity.red_primary.x<0.6401f &&
2618 image->chromaticity.red_primary.y>0.3299f &&
2619 image->chromaticity.red_primary.y<0.3301f &&
2620 image->chromaticity.green_primary.x>0.2999f &&
2621 image->chromaticity.green_primary.x<0.3001f &&
2622 image->chromaticity.green_primary.y>0.5999f &&
2623 image->chromaticity.green_primary.y<0.6001f &&
2624 image->chromaticity.blue_primary.x>0.1499f &&
2625 image->chromaticity.blue_primary.x<0.1501f &&
2626 image->chromaticity.blue_primary.y>0.0599f &&
2627 image->chromaticity.blue_primary.y<0.0601f &&
2628 image->chromaticity.white_point.x>0.3126f &&
2629 image->chromaticity.white_point.x<0.3128f &&
2630 image->chromaticity.white_point.y>0.3289f &&
2631 image->chromaticity.white_point.y<0.3291f)
2632 ping_found_sRGB_cHRM=MagickTrue;
2635 if (image->rendering_intent != UndefinedIntent)
2637 if (ping_found_sRGB != MagickTrue &&
2638 (ping_found_gAMA != MagickTrue ||
2639 (image->gamma > .45 && image->gamma < .46)) &&
2640 (ping_found_cHRM != MagickTrue ||
2641 ping_found_sRGB_cHRM != MagickFalse) &&
2642 ping_found_iCCP != MagickTrue)
2644 png_set_sRGB(ping,ping_info,
2645 Magick_RenderingIntent_to_PNG_RenderingIntent
2646 (image->rendering_intent));
2647 file_gamma=1.000f/2.200f;
2648 ping_found_sRGB=MagickTrue;
2649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2650 " Setting sRGB as if in input");
2654 #if defined(PNG_oFFs_SUPPORTED)
2655 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2657 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2658 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2660 if (logging != MagickFalse)
2661 if (image->page.x || image->page.y)
2662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2663 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2664 image->page.x,(double) image->page.y);
2667 #if defined(PNG_pHYs_SUPPORTED)
2668 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2670 if (mng_info->have_global_phys)
2672 png_set_pHYs(ping,ping_info,
2673 mng_info->global_x_pixels_per_unit,
2674 mng_info->global_y_pixels_per_unit,
2675 mng_info->global_phys_unit_type);
2682 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2685 Set image resolution.
2687 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2689 image->resolution.x=(double) x_resolution;
2690 image->resolution.y=(double) y_resolution;
2692 if (unit_type == PNG_RESOLUTION_METER)
2694 image->units=PixelsPerCentimeterResolution;
2695 image->resolution.x=(double) x_resolution/100.0;
2696 image->resolution.y=(double) y_resolution/100.0;
2699 if (logging != MagickFalse)
2700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2701 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2702 (double) x_resolution,(double) y_resolution,unit_type);
2706 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2711 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2713 if ((number_colors == 0) &&
2714 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2716 if (mng_info->global_plte_length)
2718 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2719 (int) mng_info->global_plte_length);
2721 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2723 if (mng_info->global_trns_length)
2726 "global tRNS has more entries than global PLTE");
2730 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2731 (int) mng_info->global_trns_length,NULL);
2734 #ifdef PNG_READ_bKGD_SUPPORTED
2736 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2737 mng_info->have_saved_bkgd_index ||
2739 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2744 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2745 if (mng_info->have_saved_bkgd_index)
2746 background.index=mng_info->saved_bkgd_index;
2748 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2749 background.index=ping_background->index;
2751 background.red=(png_uint_16)
2752 mng_info->global_plte[background.index].red;
2754 background.green=(png_uint_16)
2755 mng_info->global_plte[background.index].green;
2757 background.blue=(png_uint_16)
2758 mng_info->global_plte[background.index].blue;
2760 background.gray=(png_uint_16)
2761 mng_info->global_plte[background.index].green;
2763 png_set_bKGD(ping,ping_info,&background);
2768 png_error(ping,"No global PLTE in file");
2772 #ifdef PNG_READ_bKGD_SUPPORTED
2773 if (mng_info->have_global_bkgd &&
2774 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2775 image->background_color=mng_info->mng_global_bkgd;
2777 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2782 /* Set image background color.
2783 * Scale background components to 16-bit, then scale
2789 if (ping_file_depth == 1)
2792 else if (ping_file_depth == 2)
2795 else if (ping_file_depth == 4)
2798 if (ping_file_depth <= 8)
2801 ping_background->red *= bkgd_scale;
2802 ping_background->green *= bkgd_scale;
2803 ping_background->blue *= bkgd_scale;
2805 if (logging != MagickFalse)
2807 if (logging != MagickFalse)
2808 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2810 " bkgd_scale=%d. ping_background=(%d,%d,%d).",
2811 ping_background->red,ping_background->green,
2812 ping_background->blue,
2813 bkgd_scale,ping_background->red,
2814 ping_background->green,ping_background->blue);
2817 image->background_color.red=
2818 ScaleShortToQuantum(ping_background->red);
2820 image->background_color.green=
2821 ScaleShortToQuantum(ping_background->green);
2823 image->background_color.blue=
2824 ScaleShortToQuantum(ping_background->blue);
2826 image->background_color.alpha=OpaqueAlpha;
2828 if (logging != MagickFalse)
2829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2830 " image->background_color=(%.20g,%.20g,%.20g).",
2831 (double) image->background_color.red,
2832 (double) image->background_color.green,
2833 (double) image->background_color.blue);
2835 #endif /* PNG_READ_bKGD_SUPPORTED */
2837 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2840 Image has a tRNS chunk.
2848 if (logging != MagickFalse)
2849 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2850 " Reading PNG tRNS chunk.");
2852 max_sample = (int) ((one << ping_file_depth) - 1);
2854 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2855 (int)ping_trans_color->gray > max_sample) ||
2856 (ping_color_type == PNG_COLOR_TYPE_RGB &&
2857 ((int)ping_trans_color->red > max_sample ||
2858 (int)ping_trans_color->green > max_sample ||
2859 (int)ping_trans_color->blue > max_sample)))
2861 if (logging != MagickFalse)
2862 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2863 " Ignoring PNG tRNS chunk with out-of-range sample.");
2864 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2865 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2866 image->alpha_trait=UndefinedPixelTrait;
2873 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2875 /* Scale transparent_color to short */
2876 transparent_color.red= scale_to_short*ping_trans_color->red;
2877 transparent_color.green= scale_to_short*ping_trans_color->green;
2878 transparent_color.blue= scale_to_short*ping_trans_color->blue;
2879 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2881 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2883 if (logging != MagickFalse)
2885 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2886 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
2887 (int) ping_trans_color->gray,(int) transparent_color.alpha);
2890 transparent_color.red=transparent_color.alpha;
2891 transparent_color.green=transparent_color.alpha;
2892 transparent_color.blue=transparent_color.alpha;
2896 #if defined(PNG_READ_sBIT_SUPPORTED)
2897 if (mng_info->have_global_sbit)
2899 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2900 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2903 num_passes=png_set_interlace_handling(ping);
2905 png_read_update_info(ping,ping_info);
2907 ping_rowbytes=png_get_rowbytes(ping,ping_info);
2910 Initialize image structure.
2912 mng_info->image_box.left=0;
2913 mng_info->image_box.right=(ssize_t) ping_width;
2914 mng_info->image_box.top=0;
2915 mng_info->image_box.bottom=(ssize_t) ping_height;
2916 if (mng_info->mng_type == 0)
2918 mng_info->mng_width=ping_width;
2919 mng_info->mng_height=ping_height;
2920 mng_info->frame=mng_info->image_box;
2921 mng_info->clip=mng_info->image_box;
2926 image->page.y=mng_info->y_off[mng_info->object_id];
2929 image->compression=ZipCompression;
2930 image->columns=ping_width;
2931 image->rows=ping_height;
2933 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2934 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2937 image_gamma = image->gamma;
2939 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2940 " image->gamma=%f",(float) image_gamma);
2942 if (image_gamma > 0.75)
2944 /* Set image->rendering_intent to Undefined,
2945 * image->colorspace to GRAY, and reset image->chromaticity.
2947 image->intensity = Rec709LuminancePixelIntensityMethod;
2948 SetImageColorspace(image,GRAYColorspace,exception);
2953 save_rendering_intent = image->rendering_intent;
2955 save_chromaticity = image->chromaticity;
2957 SetImageColorspace(image,GRAYColorspace,exception);
2958 image->rendering_intent = save_rendering_intent;
2959 image->chromaticity = save_chromaticity;
2962 image->gamma = image_gamma;
2965 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2966 " image->colorspace=%d",(int) image->colorspace);
2968 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2969 ((int) ping_bit_depth < 16 &&
2970 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2975 image->storage_class=PseudoClass;
2977 image->colors=one << ping_file_depth;
2978 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2979 if (image->colors > 256)
2982 if (image->colors > 65536L)
2983 image->colors=65536L;
2985 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2990 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2991 image->colors=(size_t) number_colors;
2993 if (logging != MagickFalse)
2994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2995 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2999 if (image->storage_class == PseudoClass)
3002 Initialize image colormap.
3004 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3005 png_error(ping,"Memory allocation failed");
3007 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3012 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3014 for (i=0; i < (ssize_t) number_colors; i++)
3016 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3017 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3018 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3021 for ( ; i < (ssize_t) image->colors; i++)
3023 image->colormap[i].red=0;
3024 image->colormap[i].green=0;
3025 image->colormap[i].blue=0;
3034 scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3036 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3037 scale = ScaleShortToQuantum(scale);
3040 for (i=0; i < (ssize_t) image->colors; i++)
3042 image->colormap[i].red=(Quantum) (i*scale);
3043 image->colormap[i].green=(Quantum) (i*scale);
3044 image->colormap[i].blue=(Quantum) (i*scale);
3049 /* Set some properties for reporting by "identify" */
3052 msg[MagickPathExtent];
3054 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3055 ping_interlace_method in value */
3057 (void) FormatLocaleString(msg,MagickPathExtent,
3058 "%d, %d",(int) ping_width, (int) ping_height);
3059 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3061 (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_file_depth);
3062 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3064 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3065 (int) ping_color_type,
3066 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3067 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3069 if (ping_interlace_method == 0)
3071 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3072 (int) ping_interlace_method);
3074 else if (ping_interlace_method == 1)
3076 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3077 (int) ping_interlace_method);
3081 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3082 (int) ping_interlace_method);
3084 (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3086 if (number_colors != 0)
3088 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3089 (int) number_colors);
3090 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3094 #if defined(PNG_tIME_SUPPORTED)
3095 read_tIME_chunk(image,ping,ping_info,exception);
3100 Read image scanlines.
3102 if (image->delay != 0)
3103 mng_info->scenes_found++;
3105 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3106 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3107 (image_info->first_scene+image_info->number_scenes))))
3109 /* This happens later in non-ping decodes */
3110 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3111 image->storage_class=DirectClass;
3113 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3114 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3115 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3116 BlendPixelTrait : UndefinedPixelTrait;
3118 if (logging != MagickFalse)
3119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3120 " Skipping PNG image data for scene %.20g",(double)
3121 mng_info->scenes_found-1);
3122 png_destroy_read_struct(&ping,&ping_info,&end_info);
3124 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3125 UnlockSemaphoreInfo(ping_semaphore);
3128 if (logging != MagickFalse)
3129 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3130 " exit ReadOnePNGImage().");
3135 if (logging != MagickFalse)
3136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3137 " Reading PNG IDAT chunk(s)");
3139 status=SetImageExtent(image,image->columns,image->rows,exception);
3140 if (status == MagickFalse)
3141 return(DestroyImageList(image));
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 (num_passes == 1)
3265 status=SetImageProgress(image,LoadImageTag,
3266 (MagickOffsetType) y, image->rows);
3268 if (status == MagickFalse)
3271 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3275 if (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)
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 (num_passes == 1)
3442 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3445 if (status == MagickFalse)
3450 if (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 (strlen(text[i].key) > 16 &&
3624 memcmp(text[i].key, "Raw profile type ",17) == 0)
3629 value=GetImageOption(image_info,"profile:skip");
3631 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3633 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3636 if (logging != MagickFalse)
3637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3638 " Read raw profile %s",text[i].key+17);
3642 if (logging != MagickFalse)
3643 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3644 " Skipping raw profile %s",text[i].key+17);
3653 length=text[i].text_length;
3654 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3656 if (value == (char *) NULL)
3658 png_error(ping,"Memory allocation failed");
3662 (void) ConcatenateMagickString(value,text[i].text,length+2);
3664 /* Don't save "density" or "units" property if we have a pHYs
3667 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3668 (LocaleCompare(text[i].key,"density") != 0 &&
3669 LocaleCompare(text[i].key,"units") != 0))
3670 (void) SetImageProperty(image,text[i].key,value,exception);
3672 if (logging != MagickFalse)
3674 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3677 (unsigned long) length,
3681 value=DestroyString(value);
3684 num_text_total += num_text;
3687 #ifdef MNG_OBJECT_BUFFERS
3689 Store the object if necessary.
3691 if (object_id && !mng_info->frozen[object_id])
3693 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3696 create a new object buffer.
3698 mng_info->ob[object_id]=(MngBuffer *)
3699 AcquireMagickMemory(sizeof(MngBuffer));
3701 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3703 mng_info->ob[object_id]->image=(Image *) NULL;
3704 mng_info->ob[object_id]->reference_count=1;
3708 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3709 mng_info->ob[object_id]->frozen)
3711 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3712 png_error(ping,"Memory allocation failed");
3714 if (mng_info->ob[object_id]->frozen)
3715 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3721 if (mng_info->ob[object_id]->image != (Image *) NULL)
3722 mng_info->ob[object_id]->image=DestroyImage
3723 (mng_info->ob[object_id]->image);
3725 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3728 if (mng_info->ob[object_id]->image != (Image *) NULL)
3729 mng_info->ob[object_id]->image->file=(FILE *) NULL;
3732 png_error(ping, "Cloning image for object buffer failed");
3734 if (ping_width > 250000L || ping_height > 250000L)
3735 png_error(ping,"PNG Image dimensions are too large.");
3737 mng_info->ob[object_id]->width=ping_width;
3738 mng_info->ob[object_id]->height=ping_height;
3739 mng_info->ob[object_id]->color_type=ping_color_type;
3740 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3741 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3742 mng_info->ob[object_id]->compression_method=
3743 ping_compression_method;
3744 mng_info->ob[object_id]->filter_method=ping_filter_method;
3746 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3752 Copy the PLTE to the object buffer.
3754 png_get_PLTE(ping,ping_info,&plte,&number_colors);
3755 mng_info->ob[object_id]->plte_length=number_colors;
3757 for (i=0; i < number_colors; i++)
3759 mng_info->ob[object_id]->plte[i]=plte[i];
3764 mng_info->ob[object_id]->plte_length=0;
3769 /* Set image->alpha_trait to MagickTrue if the input colortype supports
3770 * alpha or if a valid tRNS chunk is present, no matter whether there
3771 * is actual transparency present.
3773 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3774 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3775 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3776 BlendPixelTrait : UndefinedPixelTrait;
3778 #if 0 /* I'm not sure what's wrong here but it does not work. */
3779 if (image->alpha_trait != UndefinedPixelTrait)
3781 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3782 (void) SetImageType(image,GrayscaleAlphaType,exception);
3784 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3785 (void) SetImageType(image,PaletteAlphaType,exception);
3788 (void) SetImageType(image,TrueColorAlphaType,exception);
3793 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3794 (void) SetImageType(image,GrayscaleType,exception);
3796 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3797 (void) SetImageType(image,PaletteType,exception);
3800 (void) SetImageType(image,TrueColorType,exception);
3804 /* Set more properties for identify to retrieve */
3807 msg[MagickPathExtent];
3809 if (num_text_total != 0)
3811 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3812 (void) FormatLocaleString(msg,MagickPathExtent,
3813 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3814 (void) SetImageProperty(image,"png:text",msg,
3818 if (num_raw_profiles != 0)
3820 (void) FormatLocaleString(msg,MagickPathExtent,
3821 "%d were found", num_raw_profiles);
3822 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3826 if (ping_found_cHRM != MagickFalse)
3828 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3829 "chunk was found (see Chromaticity, above)");
3830 (void) SetImageProperty(image,"png:cHRM",msg,
3834 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3836 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3837 "chunk was found (see Background color, above)");
3838 (void) SetImageProperty(image,"png:bKGD",msg,
3842 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3845 #if defined(PNG_iCCP_SUPPORTED)
3846 if (ping_found_iCCP != MagickFalse)
3847 (void) SetImageProperty(image,"png:iCCP",msg,
3851 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3852 (void) SetImageProperty(image,"png:tRNS",msg,
3855 #if defined(PNG_sRGB_SUPPORTED)
3856 if (ping_found_sRGB != MagickFalse)
3858 (void) FormatLocaleString(msg,MagickPathExtent,
3861 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3862 (void) SetImageProperty(image,"png:sRGB",msg,
3867 if (ping_found_gAMA != MagickFalse)
3869 (void) FormatLocaleString(msg,MagickPathExtent,
3870 "gamma=%.8g (See Gamma, above)",
3872 (void) SetImageProperty(image,"png:gAMA",msg,
3876 #if defined(PNG_pHYs_SUPPORTED)
3877 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3879 (void) FormatLocaleString(msg,MagickPathExtent,
3880 "x_res=%.10g, y_res=%.10g, units=%d",
3881 (double) x_resolution,(double) y_resolution, unit_type);
3882 (void) SetImageProperty(image,"png:pHYs",msg,
3887 #if defined(PNG_oFFs_SUPPORTED)
3888 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3890 (void) FormatLocaleString(msg,MagickPathExtent,"x_off=%.20g, y_off=%.20g",
3891 (double) image->page.x,(double) image->page.y);
3892 (void) SetImageProperty(image,"png:oFFs",msg,
3897 #if defined(PNG_tIME_SUPPORTED)
3898 read_tIME_chunk(image,ping,end_info,exception);
3901 if ((image->page.width != 0 && image->page.width != image->columns) ||
3902 (image->page.height != 0 && image->page.height != image->rows))
3904 (void) FormatLocaleString(msg,MagickPathExtent,
3905 "width=%.20g, height=%.20g",
3906 (double) image->page.width,(double) image->page.height);
3907 (void) SetImageProperty(image,"png:vpAg",msg,
3913 Relinquish resources.
3915 png_destroy_read_struct(&ping,&ping_info,&end_info);
3917 pixel_info=RelinquishVirtualMemory(pixel_info);
3919 if (logging != MagickFalse)
3920 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3921 " exit ReadOnePNGImage()");
3923 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3924 UnlockSemaphoreInfo(ping_semaphore);
3927 /* } for navigation to beginning of SETJMP-protected block, revert to
3928 * Throwing an Exception when an error occurs.
3933 /* end of reading one PNG image */
3936 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3950 magic_number[MagickPathExtent];
3958 assert(image_info != (const ImageInfo *) NULL);
3959 assert(image_info->signature == MagickCoreSignature);
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 == MagickCoreSignature);
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;
3999 image=ReadOnePNGImage(mng_info,image_info,exception);
4000 MngInfoFreeStruct(mng_info,&have_mng_structure);
4002 if (image == (Image *) NULL)
4004 if (logging != MagickFalse)
4005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4006 "exit ReadPNGImage() with error");
4008 return((Image *) NULL);
4011 (void) CloseBlob(image);
4013 if ((image->columns == 0) || (image->rows == 0))
4015 if (logging != MagickFalse)
4016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4017 "exit ReadPNGImage() with error.");
4019 ThrowReaderException(CorruptImageError,"CorruptImage");
4022 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4023 ((image->gamma < .45) || (image->gamma > .46)) &&
4024 !(image->chromaticity.red_primary.x>0.6399f &&
4025 image->chromaticity.red_primary.x<0.6401f &&
4026 image->chromaticity.red_primary.y>0.3299f &&
4027 image->chromaticity.red_primary.y<0.3301f &&
4028 image->chromaticity.green_primary.x>0.2999f &&
4029 image->chromaticity.green_primary.x<0.3001f &&
4030 image->chromaticity.green_primary.y>0.5999f &&
4031 image->chromaticity.green_primary.y<0.6001f &&
4032 image->chromaticity.blue_primary.x>0.1499f &&
4033 image->chromaticity.blue_primary.x<0.1501f &&
4034 image->chromaticity.blue_primary.y>0.0599f &&
4035 image->chromaticity.blue_primary.y<0.0601f &&
4036 image->chromaticity.white_point.x>0.3126f &&
4037 image->chromaticity.white_point.x<0.3128f &&
4038 image->chromaticity.white_point.y>0.3289f &&
4039 image->chromaticity.white_point.y<0.3291f))
4041 SetImageColorspace(image,RGBColorspace,exception);
4044 if (logging != MagickFalse)
4046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4047 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4048 (double) image->page.width,(double) image->page.height,
4049 (double) image->page.x,(double) image->page.y);
4050 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4051 " image->colorspace: %d", (int) image->colorspace);
4054 if (logging != MagickFalse)
4055 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4062 #if defined(JNG_SUPPORTED)
4064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4068 % R e a d O n e J N G I m a g e %
4072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4074 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4075 % (minus the 8-byte signature) and returns it. It allocates the memory
4076 % necessary for the new Image structure and returns a pointer to the new
4079 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4081 % The format of the ReadOneJNGImage method is:
4083 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4084 % ExceptionInfo *exception)
4086 % A description of each parameter follows:
4088 % o mng_info: Specifies a pointer to a MngInfo structure.
4090 % o image_info: the image info.
4092 % o exception: return any errors or warnings in this structure.
4095 static Image *ReadOneJNGImage(MngInfo *mng_info,
4096 const ImageInfo *image_info, ExceptionInfo *exception)
4123 jng_image_sample_depth,
4124 jng_image_compression_method,
4125 jng_image_interlace_method,
4126 jng_alpha_sample_depth,
4127 jng_alpha_compression_method,
4128 jng_alpha_filter_method,
4129 jng_alpha_interlace_method;
4131 register const Quantum
4141 register unsigned char
4151 jng_alpha_compression_method=0;
4152 jng_alpha_sample_depth=8;
4156 alpha_image=(Image *) NULL;
4157 color_image=(Image *) NULL;
4158 alpha_image_info=(ImageInfo *) NULL;
4159 color_image_info=(ImageInfo *) NULL;
4161 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4162 " Enter ReadOneJNGImage()");
4164 image=mng_info->image;
4166 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4169 Allocate next image structure.
4171 if (logging != MagickFalse)
4172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4173 " AcquireNextImage()");
4175 AcquireNextImage(image_info,image,exception);
4177 if (GetNextImageInList(image) == (Image *) NULL)
4178 return((Image *) NULL);
4180 image=SyncNextImageInList(image);
4182 mng_info->image=image;
4185 Signature bytes have already been read.
4188 read_JSEP=MagickFalse;
4189 reading_idat=MagickFalse;
4193 type[MagickPathExtent];
4202 Read a new JNG chunk.
4204 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4205 2*GetBlobSize(image));
4207 if (status == MagickFalse)
4211 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4212 length=ReadBlobMSBLong(image);
4213 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4215 if (logging != MagickFalse)
4216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4217 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4218 type[0],type[1],type[2],type[3],(double) length);
4220 if (length > PNG_UINT_31_MAX || count == 0)
4221 ThrowReaderException(CorruptImageError,"CorruptImage");
4224 chunk=(unsigned char *) NULL;
4228 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4230 if (chunk == (unsigned char *) NULL)
4231 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4233 for (i=0; i < (ssize_t) length; i++)
4234 chunk[i]=(unsigned char) ReadBlobByte(image);
4239 (void) ReadBlobMSBLong(image); /* read crc word */
4241 if (memcmp(type,mng_JHDR,4) == 0)
4245 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4246 (p[2] << 8) | p[3]);
4247 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4248 (p[6] << 8) | p[7]);
4249 if ((jng_width == 0) || (jng_height == 0))
4250 ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4251 jng_color_type=p[8];
4252 jng_image_sample_depth=p[9];
4253 jng_image_compression_method=p[10];
4254 jng_image_interlace_method=p[11];
4256 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4259 jng_alpha_sample_depth=p[12];
4260 jng_alpha_compression_method=p[13];
4261 jng_alpha_filter_method=p[14];
4262 jng_alpha_interlace_method=p[15];
4264 if (logging != MagickFalse)
4266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4267 " jng_width: %16lu, jng_height: %16lu\n"
4268 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4269 " jng_image_compression_method:%3d",
4270 (unsigned long) jng_width, (unsigned long) jng_height,
4271 jng_color_type, jng_image_sample_depth,
4272 jng_image_compression_method);
4274 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4275 " jng_image_interlace_method: %3d"
4276 " jng_alpha_sample_depth: %3d",
4277 jng_image_interlace_method,
4278 jng_alpha_sample_depth);
4280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4281 " jng_alpha_compression_method:%3d\n"
4282 " jng_alpha_filter_method: %3d\n"
4283 " jng_alpha_interlace_method: %3d",
4284 jng_alpha_compression_method,
4285 jng_alpha_filter_method,
4286 jng_alpha_interlace_method);
4291 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4297 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4298 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4299 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4302 o create color_image
4303 o open color_blob, attached to color_image
4304 o if (color type has alpha)
4305 open alpha_blob, attached to alpha_image
4308 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4310 if (color_image_info == (ImageInfo *) NULL)
4311 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4313 GetImageInfo(color_image_info);
4314 color_image=AcquireImage(color_image_info,exception);
4316 if (color_image == (Image *) NULL)
4317 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4319 if (logging != MagickFalse)
4320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4321 " Creating color_blob.");
4323 (void) AcquireUniqueFilename(color_image->filename);
4324 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4327 if (status == MagickFalse)
4328 return((Image *) NULL);
4330 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4332 alpha_image_info=(ImageInfo *)
4333 AcquireMagickMemory(sizeof(ImageInfo));
4335 if (alpha_image_info == (ImageInfo *) NULL)
4336 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4338 GetImageInfo(alpha_image_info);
4339 alpha_image=AcquireImage(alpha_image_info,exception);
4341 if (alpha_image == (Image *) NULL)
4343 alpha_image=DestroyImage(alpha_image);
4344 ThrowReaderException(ResourceLimitError,
4345 "MemoryAllocationFailed");
4348 if (logging != MagickFalse)
4349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4350 " Creating alpha_blob.");
4352 (void) AcquireUniqueFilename(alpha_image->filename);
4353 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4356 if (status == MagickFalse)
4357 return((Image *) NULL);
4359 if (jng_alpha_compression_method == 0)
4364 if (logging != MagickFalse)
4365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4366 " Writing IHDR chunk to alpha_blob.");
4368 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4369 "\211PNG\r\n\032\n");
4371 (void) WriteBlobMSBULong(alpha_image,13L);
4372 PNGType(data,mng_IHDR);
4373 LogPNGChunk(logging,mng_IHDR,13L);
4374 PNGLong(data+4,jng_width);
4375 PNGLong(data+8,jng_height);
4376 data[12]=jng_alpha_sample_depth;
4377 data[13]=0; /* color_type gray */
4378 data[14]=0; /* compression method 0 */
4379 data[15]=0; /* filter_method 0 */
4380 data[16]=0; /* interlace_method 0 */
4381 (void) WriteBlob(alpha_image,17,data);
4382 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4385 reading_idat=MagickTrue;
4388 if (memcmp(type,mng_JDAT,4) == 0)
4390 /* Copy chunk to color_image->blob */
4392 if (logging != MagickFalse)
4393 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4394 " Copying JDAT chunk data to color_blob.");
4396 (void) WriteBlob(color_image,length,chunk);
4399 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4404 if (memcmp(type,mng_IDAT,4) == 0)
4409 /* Copy IDAT header and chunk data to alpha_image->blob */
4411 if (alpha_image != NULL && image_info->ping == MagickFalse)
4413 if (logging != MagickFalse)
4414 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4415 " Copying IDAT chunk data to alpha_blob.");
4417 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4418 PNGType(data,mng_IDAT);
4419 LogPNGChunk(logging,mng_IDAT,length);
4420 (void) WriteBlob(alpha_image,4,data);
4421 (void) WriteBlob(alpha_image,length,chunk);
4422 (void) WriteBlobMSBULong(alpha_image,
4423 crc32(crc32(0,data,4),chunk,(uInt) length));
4427 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4432 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4434 /* Copy chunk data to alpha_image->blob */
4436 if (alpha_image != NULL && image_info->ping == MagickFalse)
4438 if (logging != MagickFalse)
4439 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4440 " Copying JDAA chunk data to alpha_blob.");
4442 (void) WriteBlob(alpha_image,length,chunk);
4446 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4451 if (memcmp(type,mng_JSEP,4) == 0)
4453 read_JSEP=MagickTrue;
4456 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4461 if (memcmp(type,mng_bKGD,4) == 0)
4465 image->background_color.red=ScaleCharToQuantum(p[1]);
4466 image->background_color.green=image->background_color.red;
4467 image->background_color.blue=image->background_color.red;
4472 image->background_color.red=ScaleCharToQuantum(p[1]);
4473 image->background_color.green=ScaleCharToQuantum(p[3]);
4474 image->background_color.blue=ScaleCharToQuantum(p[5]);
4477 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4481 if (memcmp(type,mng_gAMA,4) == 0)
4484 image->gamma=((float) mng_get_long(p))*0.00001;
4486 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4490 if (memcmp(type,mng_cHRM,4) == 0)
4494 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4495 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4496 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4497 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4498 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4499 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4500 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4501 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4504 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4508 if (memcmp(type,mng_sRGB,4) == 0)
4512 image->rendering_intent=
4513 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4514 image->gamma=1.000f/2.200f;
4515 image->chromaticity.red_primary.x=0.6400f;
4516 image->chromaticity.red_primary.y=0.3300f;
4517 image->chromaticity.green_primary.x=0.3000f;
4518 image->chromaticity.green_primary.y=0.6000f;
4519 image->chromaticity.blue_primary.x=0.1500f;
4520 image->chromaticity.blue_primary.y=0.0600f;
4521 image->chromaticity.white_point.x=0.3127f;
4522 image->chromaticity.white_point.y=0.3290f;
4525 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4529 if (memcmp(type,mng_oFFs,4) == 0)
4533 image->page.x=(ssize_t) mng_get_long(p);
4534 image->page.y=(ssize_t) mng_get_long(&p[4]);
4536 if ((int) p[8] != 0)
4538 image->page.x/=10000;
4539 image->page.y/=10000;
4544 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4549 if (memcmp(type,mng_pHYs,4) == 0)
4553 image->resolution.x=(double) mng_get_long(p);
4554 image->resolution.y=(double) mng_get_long(&p[4]);
4555 if ((int) p[8] == PNG_RESOLUTION_METER)
4557 image->units=PixelsPerCentimeterResolution;
4558 image->resolution.x=image->resolution.x/100.0f;
4559 image->resolution.y=image->resolution.y/100.0f;
4563 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4568 if (memcmp(type,mng_iCCP,4) == 0)
4572 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4579 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4581 if (memcmp(type,mng_IEND,4))
4591 Finish up reading image data:
4593 o read main image from color_blob.
4597 o if (color_type has alpha)
4598 if alpha_encoding is PNG
4599 read secondary image from alpha_blob via ReadPNG
4600 if alpha_encoding is JPEG
4601 read secondary image from alpha_blob via ReadJPEG
4605 o copy intensity of secondary image into
4606 alpha samples of main image.
4608 o destroy the secondary image.
4611 if (color_image_info == (ImageInfo *) NULL)
4613 assert(color_image == (Image *) NULL);
4614 assert(alpha_image == (Image *) NULL);
4615 return((Image *) NULL);
4618 if (color_image == (Image *) NULL)
4620 assert(alpha_image == (Image *) NULL);
4621 return((Image *) NULL);
4624 (void) SeekBlob(color_image,0,SEEK_SET);
4626 if (logging != MagickFalse)
4627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4628 " Reading jng_image from color_blob.");
4630 assert(color_image_info != (ImageInfo *) NULL);
4631 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4632 color_image->filename);
4634 color_image_info->ping=MagickFalse; /* To do: avoid this */
4635 jng_image=ReadImage(color_image_info,exception);
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 status=SetImageExtent(image,image->columns,image->rows,exception);
4652 if (status == MagickFalse)
4653 return(DestroyImageList(image));
4655 for (y=0; y < (ssize_t) image->rows; y++)
4657 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4658 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4659 for (x=(ssize_t) image->columns; x != 0; x--)
4661 SetPixelRed(image,GetPixelRed(jng_image,s),q);
4662 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4663 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4664 q+=GetPixelChannels(image);
4665 s+=GetPixelChannels(jng_image);
4668 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4672 jng_image=DestroyImage(jng_image);
4674 if (image_info->ping == MagickFalse)
4676 if (jng_color_type >= 12)
4678 if (jng_alpha_compression_method == 0)
4682 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4683 PNGType(data,mng_IEND);
4684 LogPNGChunk(logging,mng_IEND,0L);
4685 (void) WriteBlob(alpha_image,4,data);
4686 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4689 (void) CloseBlob(alpha_image);
4691 if (logging != MagickFalse)
4692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4693 " Reading alpha from alpha_blob.");
4695 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
4696 "%s",alpha_image->filename);
4698 jng_image=ReadImage(alpha_image_info,exception);
4700 if (jng_image != (Image *) NULL)
4701 for (y=0; y < (ssize_t) image->rows; y++)
4703 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4705 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4707 if (image->alpha_trait != UndefinedPixelTrait)
4708 for (x=(ssize_t) image->columns; x != 0; x--)
4710 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4711 q+=GetPixelChannels(image);
4712 s+=GetPixelChannels(jng_image);
4716 for (x=(ssize_t) image->columns; x != 0; x--)
4718 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4719 if (GetPixelAlpha(image,q) != OpaqueAlpha)
4720 image->alpha_trait=BlendPixelTrait;
4721 q+=GetPixelChannels(image);
4722 s+=GetPixelChannels(jng_image);
4725 if (SyncAuthenticPixels(image,exception) == MagickFalse)
4728 (void) RelinquishUniqueFileResource(alpha_image->filename);
4729 alpha_image=DestroyImage(alpha_image);
4730 alpha_image_info=DestroyImageInfo(alpha_image_info);
4731 if (jng_image != (Image *) NULL)
4732 jng_image=DestroyImage(jng_image);
4736 /* Read the JNG image. */
4738 if (mng_info->mng_type == 0)
4740 mng_info->mng_width=jng_width;
4741 mng_info->mng_height=jng_height;
4744 if (image->page.width == 0 && image->page.height == 0)
4746 image->page.width=jng_width;
4747 image->page.height=jng_height;
4750 if (image->page.x == 0 && image->page.y == 0)
4752 image->page.x=mng_info->x_off[mng_info->object_id];
4753 image->page.y=mng_info->y_off[mng_info->object_id];
4758 image->page.y=mng_info->y_off[mng_info->object_id];
4761 mng_info->image_found++;
4762 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4763 2*GetBlobSize(image));
4765 if (status == MagickFalse)
4766 return((Image *) NULL);
4768 if (logging != MagickFalse)
4769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4770 " exit ReadOneJNGImage()");
4776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4780 % R e a d J N G I m a g e %
4784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4786 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4787 % (including the 8-byte signature) and returns it. It allocates the memory
4788 % necessary for the new Image structure and returns a pointer to the new
4791 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4793 % The format of the ReadJNGImage method is:
4795 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4798 % A description of each parameter follows:
4800 % o image_info: the image info.
4802 % o exception: return any errors or warnings in this structure.
4806 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4820 magic_number[MagickPathExtent];
4828 assert(image_info != (const ImageInfo *) NULL);
4829 assert(image_info->signature == MagickCoreSignature);
4830 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4831 assert(exception != (ExceptionInfo *) NULL);
4832 assert(exception->signature == MagickCoreSignature);
4833 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4834 image=AcquireImage(image_info,exception);
4835 mng_info=(MngInfo *) NULL;
4836 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4838 if (status == MagickFalse)
4839 return((Image *) NULL);
4841 if (LocaleCompare(image_info->magick,"JNG") != 0)
4842 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4844 /* Verify JNG signature. */
4846 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4848 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4849 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4851 /* Allocate a MngInfo structure. */
4853 have_mng_structure=MagickFalse;
4854 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4856 if (mng_info == (MngInfo *) NULL)
4857 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4859 /* Initialize members of the MngInfo structure. */
4861 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4862 have_mng_structure=MagickTrue;
4864 mng_info->image=image;
4865 image=ReadOneJNGImage(mng_info,image_info,exception);
4866 MngInfoFreeStruct(mng_info,&have_mng_structure);
4868 if (image == (Image *) NULL)
4870 if (logging != MagickFalse)
4871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4872 "exit ReadJNGImage() with error");
4874 return((Image *) NULL);
4876 (void) CloseBlob(image);
4878 if (image->columns == 0 || image->rows == 0)
4880 if (logging != MagickFalse)
4881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4882 "exit ReadJNGImage() with error");
4884 ThrowReaderException(CorruptImageError,"CorruptImage");
4887 if (logging != MagickFalse)
4888 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4894 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4897 page_geometry[MagickPathExtent];
4929 #if defined(MNG_INSERT_LAYERS)
4931 mng_background_color;
4934 register unsigned char
4949 #if defined(MNG_INSERT_LAYERS)
4954 volatile unsigned int
4955 #ifdef MNG_OBJECT_BUFFERS
4956 mng_background_object=0,
4958 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4961 default_frame_timeout,
4963 #if defined(MNG_INSERT_LAYERS)
4969 /* These delays are all measured in image ticks_per_second,
4970 * not in MNG ticks_per_second
4973 default_frame_delay,
4977 #if defined(MNG_INSERT_LAYERS)
4986 previous_fb.bottom=0;
4988 previous_fb.right=0;
4990 default_fb.bottom=0;
4994 /* Open image file. */
4996 assert(image_info != (const ImageInfo *) NULL);
4997 assert(image_info->signature == MagickCoreSignature);
4998 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4999 assert(exception != (ExceptionInfo *) NULL);
5000 assert(exception->signature == MagickCoreSignature);
5001 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5002 image=AcquireImage(image_info,exception);
5003 mng_info=(MngInfo *) NULL;
5004 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5006 if (status == MagickFalse)
5007 return((Image *) NULL);
5009 first_mng_object=MagickFalse;
5011 have_mng_structure=MagickFalse;
5013 /* Allocate a MngInfo structure. */
5015 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5017 if (mng_info == (MngInfo *) NULL)
5018 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5020 /* Initialize members of the MngInfo structure. */
5022 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5023 mng_info->image=image;
5024 have_mng_structure=MagickTrue;
5026 if (LocaleCompare(image_info->magick,"MNG") == 0)
5029 magic_number[MagickPathExtent];
5031 /* Verify MNG signature. */
5032 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5033 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5034 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5036 /* Initialize some nonzero members of the MngInfo structure. */
5037 for (i=0; i < MNG_MAX_OBJECTS; i++)
5039 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5040 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5042 mng_info->exists[0]=MagickTrue;
5045 first_mng_object=MagickTrue;
5047 #if defined(MNG_INSERT_LAYERS)
5048 insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5050 default_frame_delay=0;
5051 default_frame_timeout=0;
5054 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5056 skip_to_iend=MagickFalse;
5057 term_chunk_found=MagickFalse;
5058 mng_info->framing_mode=1;
5059 #if defined(MNG_INSERT_LAYERS)
5060 mandatory_back=MagickFalse;
5062 #if defined(MNG_INSERT_LAYERS)
5063 mng_background_color=image->background_color;
5065 default_fb=mng_info->frame;
5066 previous_fb=mng_info->frame;
5070 type[MagickPathExtent];
5072 if (LocaleCompare(image_info->magick,"MNG") == 0)
5081 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5082 length=ReadBlobMSBLong(image);
5083 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5085 if (logging != MagickFalse)
5086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5087 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5088 type[0],type[1],type[2],type[3],(double) length);
5090 if (length > PNG_UINT_31_MAX)
5097 ThrowReaderException(CorruptImageError,"CorruptImage");
5100 chunk=(unsigned char *) NULL;
5104 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5106 if (chunk == (unsigned char *) NULL)
5107 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5109 for (i=0; i < (ssize_t) length; i++)
5110 chunk[i]=(unsigned char) ReadBlobByte(image);
5115 (void) ReadBlobMSBLong(image); /* read crc word */
5117 #if !defined(JNG_SUPPORTED)
5118 if (memcmp(type,mng_JHDR,4) == 0)
5120 skip_to_iend=MagickTrue;
5122 if (mng_info->jhdr_warning == 0)
5123 (void) ThrowMagickException(exception,GetMagickModule(),
5124 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5126 mng_info->jhdr_warning++;
5129 if (memcmp(type,mng_DHDR,4) == 0)
5131 skip_to_iend=MagickTrue;
5133 if (mng_info->dhdr_warning == 0)
5134 (void) ThrowMagickException(exception,GetMagickModule(),
5135 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5137 mng_info->dhdr_warning++;
5139 if (memcmp(type,mng_MEND,4) == 0)
5144 if (memcmp(type,mng_IEND,4) == 0)
5145 skip_to_iend=MagickFalse;
5148 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5150 if (logging != MagickFalse)
5151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5157 if (memcmp(type,mng_MHDR,4) == 0)
5162 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5163 ThrowReaderException(CorruptImageError,"CorruptImage");
5166 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5167 (p[2] << 8) | p[3]);
5169 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5170 (p[6] << 8) | p[7]);
5172 if (logging != MagickFalse)
5174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5175 " MNG width: %.20g",(double) mng_info->mng_width);
5176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5177 " MNG height: %.20g",(double) mng_info->mng_height);
5181 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5183 if (mng_info->ticks_per_second == 0)
5184 default_frame_delay=0;
5187 default_frame_delay=1UL*image->ticks_per_second/
5188 mng_info->ticks_per_second;
5190 frame_delay=default_frame_delay;
5194 simplicity=(size_t) mng_get_long(p);
5196 mng_type=1; /* Full MNG */
5198 if ((simplicity != 0) && ((simplicity | 11) == 11))
5199 mng_type=2; /* LC */
5201 if ((simplicity != 0) && ((simplicity | 9) == 9))
5202 mng_type=3; /* VLC */
5204 #if defined(MNG_INSERT_LAYERS)
5206 insert_layers=MagickTrue;
5208 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5210 /* Allocate next image structure. */
5211 AcquireNextImage(image_info,image,exception);
5213 if (GetNextImageInList(image) == (Image *) NULL)
5214 return((Image *) NULL);
5216 image=SyncNextImageInList(image);
5217 mng_info->image=image;
5220 if ((mng_info->mng_width > 65535L) ||
5221 (mng_info->mng_height > 65535L))
5222 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5224 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5225 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5226 mng_info->mng_height);
5228 mng_info->frame.left=0;
5229 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5230 mng_info->frame.top=0;
5231 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5232 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5234 for (i=0; i < MNG_MAX_OBJECTS; i++)
5235 mng_info->object_clip[i]=mng_info->frame;
5237 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5241 if (memcmp(type,mng_TERM,4) == 0)
5251 final_delay=(png_uint_32) mng_get_long(&p[2]);
5252 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5254 if (mng_iterations == PNG_UINT_31_MAX)
5257 image->iterations=mng_iterations;
5258 term_chunk_found=MagickTrue;
5261 if (logging != MagickFalse)
5263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5264 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5265 repeat,(double) final_delay, (double) image->iterations);
5268 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5271 if (memcmp(type,mng_DEFI,4) == 0)
5274 (void) ThrowMagickException(exception,GetMagickModule(),
5275 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5281 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5282 ThrowReaderException(CorruptImageError,"CorruptImage");
5285 object_id=(p[0] << 8) | p[1];
5287 if (mng_type == 2 && object_id != 0)
5288 (void) ThrowMagickException(exception,GetMagickModule(),
5289 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5292 if (object_id > MNG_MAX_OBJECTS)
5295 Instead of using a warning we should allocate a larger
5296 MngInfo structure and continue.
5298 (void) ThrowMagickException(exception,GetMagickModule(),
5299 CoderError,"object id too large","`%s'",image->filename);
5300 object_id=MNG_MAX_OBJECTS;
5303 if (mng_info->exists[object_id])
5304 if (mng_info->frozen[object_id])
5306 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5307 (void) ThrowMagickException(exception,
5308 GetMagickModule(),CoderError,
5309 "DEFI cannot redefine a frozen MNG object","`%s'",
5314 mng_info->exists[object_id]=MagickTrue;
5317 mng_info->invisible[object_id]=p[2];
5320 Extract object offset info.
5324 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5325 (p[5] << 16) | (p[6] << 8) | p[7]);
5327 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5328 (p[9] << 16) | (p[10] << 8) | p[11]);
5330 if (logging != MagickFalse)
5332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5333 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5334 object_id,(double) mng_info->x_off[object_id],
5335 object_id,(double) mng_info->y_off[object_id]);
5340 Extract object clipping info.
5343 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5346 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5349 if (memcmp(type,mng_bKGD,4) == 0)
5351 mng_info->have_global_bkgd=MagickFalse;
5355 mng_info->mng_global_bkgd.red=
5356 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5358 mng_info->mng_global_bkgd.green=
5359 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5361 mng_info->mng_global_bkgd.blue=
5362 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5364 mng_info->have_global_bkgd=MagickTrue;
5367 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5370 if (memcmp(type,mng_BACK,4) == 0)
5372 #if defined(MNG_INSERT_LAYERS)
5374 mandatory_back=p[6];
5379 if (mandatory_back && length > 5)
5381 mng_background_color.red=
5382 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5384 mng_background_color.green=
5385 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5387 mng_background_color.blue=
5388 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5390 mng_background_color.alpha=OpaqueAlpha;
5393 #ifdef MNG_OBJECT_BUFFERS
5395 mng_background_object=(p[7] << 8) | p[8];
5398 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5402 if (memcmp(type,mng_PLTE,4) == 0)
5404 /* Read global PLTE. */
5406 if (length && (length < 769))
5408 if (mng_info->global_plte == (png_colorp) NULL)
5409 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5410 sizeof(*mng_info->global_plte));
5412 for (i=0; i < (ssize_t) (length/3); i++)
5414 mng_info->global_plte[i].red=p[3*i];
5415 mng_info->global_plte[i].green=p[3*i+1];
5416 mng_info->global_plte[i].blue=p[3*i+2];
5419 mng_info->global_plte_length=(unsigned int) (length/3);
5422 for ( ; i < 256; i++)
5424 mng_info->global_plte[i].red=i;
5425 mng_info->global_plte[i].green=i;
5426 mng_info->global_plte[i].blue=i;
5430 mng_info->global_plte_length=256;
5433 mng_info->global_plte_length=0;
5435 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5439 if (memcmp(type,mng_tRNS,4) == 0)
5441 /* read global tRNS */
5443 if (length > 0 && length < 257)
5444 for (i=0; i < (ssize_t) length; i++)
5445 mng_info->global_trns[i]=p[i];
5448 for ( ; i < 256; i++)
5449 mng_info->global_trns[i]=255;
5451 mng_info->global_trns_length=(unsigned int) length;
5452 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5455 if (memcmp(type,mng_gAMA,4) == 0)
5462 igamma=mng_get_long(p);
5463 mng_info->global_gamma=((float) igamma)*0.00001;
5464 mng_info->have_global_gama=MagickTrue;
5468 mng_info->have_global_gama=MagickFalse;
5470 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5474 if (memcmp(type,mng_cHRM,4) == 0)
5476 /* Read global cHRM */
5480 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5481 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5482 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5483 mng_info->global_chrm.red_primary.y=0.00001*
5484 mng_get_long(&p[12]);
5485 mng_info->global_chrm.green_primary.x=0.00001*
5486 mng_get_long(&p[16]);
5487 mng_info->global_chrm.green_primary.y=0.00001*
5488 mng_get_long(&p[20]);
5489 mng_info->global_chrm.blue_primary.x=0.00001*
5490 mng_get_long(&p[24]);
5491 mng_info->global_chrm.blue_primary.y=0.00001*
5492 mng_get_long(&p[28]);
5493 mng_info->have_global_chrm=MagickTrue;
5496 mng_info->have_global_chrm=MagickFalse;
5498 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5502 if (memcmp(type,mng_sRGB,4) == 0)
5509 mng_info->global_srgb_intent=
5510 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5511 mng_info->have_global_srgb=MagickTrue;
5514 mng_info->have_global_srgb=MagickFalse;
5516 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5520 if (memcmp(type,mng_iCCP,4) == 0)
5528 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5533 if (memcmp(type,mng_FRAM,4) == 0)
5536 (void) ThrowMagickException(exception,GetMagickModule(),
5537 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5540 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5541 image->delay=frame_delay;
5543 frame_delay=default_frame_delay;
5544 frame_timeout=default_frame_timeout;
5549 mng_info->framing_mode=p[0];
5551 if (logging != MagickFalse)
5552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5553 " Framing_mode=%d",mng_info->framing_mode);
5557 /* Note the delay and frame clipping boundaries. */
5559 p++; /* framing mode */
5561 while (*p && ((p-chunk) < (ssize_t) length))
5562 p++; /* frame name */
5564 p++; /* frame name terminator */
5566 if ((p-chunk) < (ssize_t) (length-4))
5573 change_delay=(*p++);
5574 change_timeout=(*p++);
5575 change_clipping=(*p++);
5576 p++; /* change_sync */
5580 frame_delay=1UL*image->ticks_per_second*
5583 if (mng_info->ticks_per_second != 0)
5584 frame_delay/=mng_info->ticks_per_second;
5587 frame_delay=PNG_UINT_31_MAX;
5589 if (change_delay == 2)
5590 default_frame_delay=frame_delay;
5594 if (logging != MagickFalse)
5595 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5596 " Framing_delay=%.20g",(double) frame_delay);
5601 frame_timeout=1UL*image->ticks_per_second*
5604 if (mng_info->ticks_per_second != 0)
5605 frame_timeout/=mng_info->ticks_per_second;
5608 frame_timeout=PNG_UINT_31_MAX;
5610 if (change_timeout == 2)
5611 default_frame_timeout=frame_timeout;
5615 if (logging != MagickFalse)
5616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5617 " Framing_timeout=%.20g",(double) frame_timeout);
5620 if (change_clipping)
5622 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5626 if (logging != MagickFalse)
5627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5628 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5629 (double) fb.left,(double) fb.right,(double) fb.top,
5630 (double) fb.bottom);
5632 if (change_clipping == 2)
5638 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5640 subframe_width=(size_t) (mng_info->clip.right
5641 -mng_info->clip.left);
5643 subframe_height=(size_t) (mng_info->clip.bottom
5644 -mng_info->clip.top);
5646 Insert a background layer behind the frame if framing_mode is 4.
5648 #if defined(MNG_INSERT_LAYERS)
5649 if (logging != MagickFalse)
5650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5651 " subframe_width=%.20g, subframe_height=%.20g",(double)
5652 subframe_width,(double) subframe_height);
5654 if (insert_layers && (mng_info->framing_mode == 4) &&
5655 (subframe_width) && (subframe_height))
5657 /* Allocate next image structure. */
5658 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5660 AcquireNextImage(image_info,image,exception);
5662 if (GetNextImageInList(image) == (Image *) NULL)
5664 image=DestroyImageList(image);
5665 MngInfoFreeStruct(mng_info,&have_mng_structure);
5666 return((Image *) NULL);
5669 image=SyncNextImageInList(image);
5672 mng_info->image=image;
5674 if (term_chunk_found)
5676 image->start_loop=MagickTrue;
5677 image->iterations=mng_iterations;
5678 term_chunk_found=MagickFalse;
5682 image->start_loop=MagickFalse;
5684 image->columns=subframe_width;
5685 image->rows=subframe_height;
5686 image->page.width=subframe_width;
5687 image->page.height=subframe_height;
5688 image->page.x=mng_info->clip.left;
5689 image->page.y=mng_info->clip.top;
5690 image->background_color=mng_background_color;
5691 image->alpha_trait=UndefinedPixelTrait;
5693 (void) SetImageBackgroundColor(image,exception);
5695 if (logging != MagickFalse)
5696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5697 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5698 (double) mng_info->clip.left,(double) mng_info->clip.right,
5699 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5702 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5706 if (memcmp(type,mng_CLIP,4) == 0)
5717 first_object=(p[0] << 8) | p[1];
5718 last_object=(p[2] << 8) | p[3];
5721 for (i=(int) first_object; i <= (int) last_object; i++)
5723 if (mng_info->exists[i] && !mng_info->frozen[i])
5728 box=mng_info->object_clip[i];
5729 if ((p-chunk) < (ssize_t) (length-17))
5730 mng_info->object_clip[i]=
5731 mng_read_box(box,(char) p[0],&p[1]);
5736 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5740 if (memcmp(type,mng_SAVE,4) == 0)
5742 for (i=1; i < MNG_MAX_OBJECTS; i++)
5743 if (mng_info->exists[i])
5745 mng_info->frozen[i]=MagickTrue;
5746 #ifdef MNG_OBJECT_BUFFERS
5747 if (mng_info->ob[i] != (MngBuffer *) NULL)
5748 mng_info->ob[i]->frozen=MagickTrue;
5753 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5758 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5760 /* Read DISC or SEEK. */
5762 if ((length == 0) || !memcmp(type,mng_SEEK,4))
5764 for (i=1; i < MNG_MAX_OBJECTS; i++)
5765 MngInfoDiscardObject(mng_info,i);
5773 for (j=1; j < (ssize_t) length; j+=2)
5775 i=p[j-1] << 8 | p[j];
5776 MngInfoDiscardObject(mng_info,i);
5781 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5786 if (memcmp(type,mng_MOVE,4) == 0)
5796 first_object=(p[0] << 8) | p[1];
5797 last_object=(p[2] << 8) | p[3];
5800 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5802 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5803 (p-chunk) < (ssize_t) (length-8))
5811 old_pair.a=mng_info->x_off[i];
5812 old_pair.b=mng_info->y_off[i];
5813 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
5814 mng_info->x_off[i]=new_pair.a;
5815 mng_info->y_off[i]=new_pair.b;
5820 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5824 if (memcmp(type,mng_LOOP,4) == 0)
5826 ssize_t loop_iters=1;
5829 loop_level=chunk[0];
5830 mng_info->loop_active[loop_level]=1; /* mark loop active */
5832 /* Record starting point. */
5833 loop_iters=mng_get_long(&chunk[1]);
5835 if (logging != MagickFalse)
5836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5837 " LOOP level %.20g has %.20g iterations ",
5838 (double) loop_level, (double) loop_iters);
5840 if (loop_iters == 0)
5841 skipping_loop=loop_level;
5845 mng_info->loop_jump[loop_level]=TellBlob(image);
5846 mng_info->loop_count[loop_level]=loop_iters;
5849 mng_info->loop_iteration[loop_level]=0;
5851 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5855 if (memcmp(type,mng_ENDL,4) == 0)
5859 loop_level=chunk[0];
5861 if (skipping_loop > 0)
5863 if (skipping_loop == loop_level)
5866 Found end of zero-iteration loop.
5869 mng_info->loop_active[loop_level]=0;
5875 if (mng_info->loop_active[loop_level] == 1)
5877 mng_info->loop_count[loop_level]--;
5878 mng_info->loop_iteration[loop_level]++;
5880 if (logging != MagickFalse)
5881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5882 " ENDL: LOOP level %.20g has %.20g remaining iters ",
5883 (double) loop_level,(double)
5884 mng_info->loop_count[loop_level]);
5886 if (mng_info->loop_count[loop_level] != 0)
5889 SeekBlob(image,mng_info->loop_jump[loop_level],
5893 ThrowReaderException(CorruptImageError,
5894 "ImproperImageHeader");
5905 mng_info->loop_active[loop_level]=0;
5907 for (i=0; i < loop_level; i++)
5908 if (mng_info->loop_active[i] == 1)
5909 last_level=(short) i;
5910 loop_level=last_level;
5916 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5920 if (memcmp(type,mng_CLON,4) == 0)
5922 if (mng_info->clon_warning == 0)
5923 (void) ThrowMagickException(exception,GetMagickModule(),
5924 CoderError,"CLON is not implemented yet","`%s'",
5927 mng_info->clon_warning++;
5930 if (memcmp(type,mng_MAGN,4) == 0)
5945 magn_first=(p[0] << 8) | p[1];
5951 magn_last=(p[2] << 8) | p[3];
5954 magn_last=magn_first;
5955 #ifndef MNG_OBJECT_BUFFERS
5956 if (magn_first || magn_last)
5957 if (mng_info->magn_warning == 0)
5959 (void) ThrowMagickException(exception,
5960 GetMagickModule(),CoderError,
5961 "MAGN is not implemented yet for nonzero objects",
5962 "`%s'",image->filename);
5964 mng_info->magn_warning++;
5974 magn_mx=(p[5] << 8) | p[6];
5983 magn_my=(p[7] << 8) | p[8];
5992 magn_ml=(p[9] << 8) | p[10];
6001 magn_mr=(p[11] << 8) | p[12];
6010 magn_mt=(p[13] << 8) | p[14];
6019 magn_mb=(p[15] << 8) | p[16];
6031 magn_methy=magn_methx;
6034 if (magn_methx > 5 || magn_methy > 5)
6035 if (mng_info->magn_warning == 0)
6037 (void) ThrowMagickException(exception,
6038 GetMagickModule(),CoderError,
6039 "Unknown MAGN method in MNG datastream","`%s'",
6042 mng_info->magn_warning++;
6044 #ifdef MNG_OBJECT_BUFFERS
6045 /* Magnify existing objects in the range magn_first to magn_last */
6047 if (magn_first == 0 || magn_last == 0)
6049 /* Save the magnification factors for object 0 */
6050 mng_info->magn_mb=magn_mb;
6051 mng_info->magn_ml=magn_ml;
6052 mng_info->magn_mr=magn_mr;
6053 mng_info->magn_mt=magn_mt;
6054 mng_info->magn_mx=magn_mx;
6055 mng_info->magn_my=magn_my;
6056 mng_info->magn_methx=magn_methx;
6057 mng_info->magn_methy=magn_methy;
6061 if (memcmp(type,mng_PAST,4) == 0)
6063 if (mng_info->past_warning == 0)
6064 (void) ThrowMagickException(exception,GetMagickModule(),
6065 CoderError,"PAST is not implemented yet","`%s'",
6068 mng_info->past_warning++;
6071 if (memcmp(type,mng_SHOW,4) == 0)
6073 if (mng_info->show_warning == 0)
6074 (void) ThrowMagickException(exception,GetMagickModule(),
6075 CoderError,"SHOW is not implemented yet","`%s'",
6078 mng_info->show_warning++;
6081 if (memcmp(type,mng_sBIT,4) == 0)
6084 mng_info->have_global_sbit=MagickFalse;
6088 mng_info->global_sbit.gray=p[0];
6089 mng_info->global_sbit.red=p[0];
6090 mng_info->global_sbit.green=p[1];
6091 mng_info->global_sbit.blue=p[2];
6092 mng_info->global_sbit.alpha=p[3];
6093 mng_info->have_global_sbit=MagickTrue;
6096 if (memcmp(type,mng_pHYs,4) == 0)
6100 mng_info->global_x_pixels_per_unit=
6101 (size_t) mng_get_long(p);
6102 mng_info->global_y_pixels_per_unit=
6103 (size_t) mng_get_long(&p[4]);
6104 mng_info->global_phys_unit_type=p[8];
6105 mng_info->have_global_phys=MagickTrue;
6109 mng_info->have_global_phys=MagickFalse;
6111 if (memcmp(type,mng_pHYg,4) == 0)
6113 if (mng_info->phyg_warning == 0)
6114 (void) ThrowMagickException(exception,GetMagickModule(),
6115 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6117 mng_info->phyg_warning++;
6119 if (memcmp(type,mng_BASI,4) == 0)
6121 skip_to_iend=MagickTrue;
6123 if (mng_info->basi_warning == 0)
6124 (void) ThrowMagickException(exception,GetMagickModule(),
6125 CoderError,"BASI is not implemented yet","`%s'",
6128 mng_info->basi_warning++;
6129 #ifdef MNG_BASI_SUPPORTED
6130 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6131 (p[2] << 8) | p[3]);
6132 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6133 (p[6] << 8) | p[7]);
6134 basi_color_type=p[8];
6135 basi_compression_method=p[9];
6136 basi_filter_type=p[10];
6137 basi_interlace_method=p[11];
6139 basi_red=(p[12] << 8) & p[13];
6145 basi_green=(p[14] << 8) & p[15];
6151 basi_blue=(p[16] << 8) & p[17];
6157 basi_alpha=(p[18] << 8) & p[19];
6161 if (basi_sample_depth == 16)
6168 basi_viewable=p[20];
6174 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6178 if (memcmp(type,mng_IHDR,4)
6179 #if defined(JNG_SUPPORTED)
6180 && memcmp(type,mng_JHDR,4)
6184 /* Not an IHDR or JHDR chunk */
6186 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6191 if (logging != MagickFalse)
6192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6193 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6195 mng_info->exists[object_id]=MagickTrue;
6196 mng_info->viewable[object_id]=MagickTrue;
6198 if (mng_info->invisible[object_id])
6200 if (logging != MagickFalse)
6201 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6202 " Skipping invisible object");
6204 skip_to_iend=MagickTrue;
6205 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6208 #if defined(MNG_INSERT_LAYERS)
6210 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6212 image_width=(size_t) mng_get_long(p);
6213 image_height=(size_t) mng_get_long(&p[4]);
6215 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6218 Insert a transparent background layer behind the entire animation
6219 if it is not full screen.
6221 #if defined(MNG_INSERT_LAYERS)
6222 if (insert_layers && mng_type && first_mng_object)
6224 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6225 (image_width < mng_info->mng_width) ||
6226 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6227 (image_height < mng_info->mng_height) ||
6228 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6230 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6233 Allocate next image structure.
6235 AcquireNextImage(image_info,image,exception);
6237 if (GetNextImageInList(image) == (Image *) NULL)
6239 image=DestroyImageList(image);
6240 MngInfoFreeStruct(mng_info,&have_mng_structure);
6241 return((Image *) NULL);
6244 image=SyncNextImageInList(image);
6246 mng_info->image=image;
6248 if (term_chunk_found)
6250 image->start_loop=MagickTrue;
6251 image->iterations=mng_iterations;
6252 term_chunk_found=MagickFalse;
6256 image->start_loop=MagickFalse;
6258 /* Make a background rectangle. */
6261 image->columns=mng_info->mng_width;
6262 image->rows=mng_info->mng_height;
6263 image->page.width=mng_info->mng_width;
6264 image->page.height=mng_info->mng_height;
6267 image->background_color=mng_background_color;
6268 (void) SetImageBackgroundColor(image,exception);
6269 if (logging != MagickFalse)
6270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6271 " Inserted transparent background layer, W=%.20g, H=%.20g",
6272 (double) mng_info->mng_width,(double) mng_info->mng_height);
6276 Insert a background layer behind the upcoming image if
6277 framing_mode is 3, and we haven't already inserted one.
6279 if (insert_layers && (mng_info->framing_mode == 3) &&
6280 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6281 (simplicity & 0x08)))
6283 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6286 Allocate next image structure.
6288 AcquireNextImage(image_info,image,exception);
6290 if (GetNextImageInList(image) == (Image *) NULL)
6292 image=DestroyImageList(image);
6293 MngInfoFreeStruct(mng_info,&have_mng_structure);
6294 return((Image *) NULL);
6297 image=SyncNextImageInList(image);
6300 mng_info->image=image;
6302 if (term_chunk_found)
6304 image->start_loop=MagickTrue;
6305 image->iterations=mng_iterations;
6306 term_chunk_found=MagickFalse;
6310 image->start_loop=MagickFalse;
6313 image->columns=subframe_width;
6314 image->rows=subframe_height;
6315 image->page.width=subframe_width;
6316 image->page.height=subframe_height;
6317 image->page.x=mng_info->clip.left;
6318 image->page.y=mng_info->clip.top;
6319 image->background_color=mng_background_color;
6320 image->alpha_trait=UndefinedPixelTrait;
6321 (void) SetImageBackgroundColor(image,exception);
6323 if (logging != MagickFalse)
6324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6325 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6326 (double) mng_info->clip.left,(double) mng_info->clip.right,
6327 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6329 #endif /* MNG_INSERT_LAYERS */
6330 first_mng_object=MagickFalse;
6332 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6335 Allocate next image structure.
6337 AcquireNextImage(image_info,image,exception);
6339 if (GetNextImageInList(image) == (Image *) NULL)
6341 image=DestroyImageList(image);
6342 MngInfoFreeStruct(mng_info,&have_mng_structure);
6343 return((Image *) NULL);
6346 image=SyncNextImageInList(image);
6348 mng_info->image=image;
6349 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6350 GetBlobSize(image));
6352 if (status == MagickFalse)
6355 if (term_chunk_found)
6357 image->start_loop=MagickTrue;
6358 term_chunk_found=MagickFalse;
6362 image->start_loop=MagickFalse;
6364 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6366 image->delay=frame_delay;
6367 frame_delay=default_frame_delay;
6373 image->page.width=mng_info->mng_width;
6374 image->page.height=mng_info->mng_height;
6375 image->page.x=mng_info->x_off[object_id];
6376 image->page.y=mng_info->y_off[object_id];
6377 image->iterations=mng_iterations;
6380 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6383 if (logging != MagickFalse)
6384 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6385 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6388 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6391 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6394 mng_info->image=image;
6395 mng_info->mng_type=mng_type;
6396 mng_info->object_id=object_id;
6398 if (memcmp(type,mng_IHDR,4) == 0)
6399 image=ReadOnePNGImage(mng_info,image_info,exception);
6401 #if defined(JNG_SUPPORTED)
6403 image=ReadOneJNGImage(mng_info,image_info,exception);
6406 if (image == (Image *) NULL)
6408 if (logging != MagickFalse)
6409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6410 "exit ReadJNGImage() with error");
6412 MngInfoFreeStruct(mng_info,&have_mng_structure);
6413 return((Image *) NULL);
6416 if (image->columns == 0 || image->rows == 0)
6418 (void) CloseBlob(image);
6419 image=DestroyImageList(image);
6420 MngInfoFreeStruct(mng_info,&have_mng_structure);
6421 return((Image *) NULL);
6424 mng_info->image=image;
6431 if (mng_info->magn_methx || mng_info->magn_methy)
6437 if (logging != MagickFalse)
6438 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6439 " Processing MNG MAGN chunk");
6441 if (mng_info->magn_methx == 1)
6443 magnified_width=mng_info->magn_ml;
6445 if (image->columns > 1)
6446 magnified_width += mng_info->magn_mr;
6448 if (image->columns > 2)
6449 magnified_width += (png_uint_32)
6450 ((image->columns-2)*(mng_info->magn_mx));
6455 magnified_width=(png_uint_32) image->columns;
6457 if (image->columns > 1)
6458 magnified_width += mng_info->magn_ml-1;
6460 if (image->columns > 2)
6461 magnified_width += mng_info->magn_mr-1;
6463 if (image->columns > 3)
6464 magnified_width += (png_uint_32)
6465 ((image->columns-3)*(mng_info->magn_mx-1));
6468 if (mng_info->magn_methy == 1)
6470 magnified_height=mng_info->magn_mt;
6472 if (image->rows > 1)
6473 magnified_height += mng_info->magn_mb;
6475 if (image->rows > 2)
6476 magnified_height += (png_uint_32)
6477 ((image->rows-2)*(mng_info->magn_my));
6482 magnified_height=(png_uint_32) image->rows;
6484 if (image->rows > 1)
6485 magnified_height += mng_info->magn_mt-1;
6487 if (image->rows > 2)
6488 magnified_height += mng_info->magn_mb-1;
6490 if (image->rows > 3)
6491 magnified_height += (png_uint_32)
6492 ((image->rows-3)*(mng_info->magn_my-1));
6495 if (magnified_height > image->rows ||
6496 magnified_width > image->columns)
6523 /* Allocate next image structure. */
6525 if (logging != MagickFalse)
6526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6527 " Allocate magnified image");
6529 AcquireNextImage(image_info,image,exception);
6531 if (GetNextImageInList(image) == (Image *) NULL)
6533 image=DestroyImageList(image);
6534 MngInfoFreeStruct(mng_info,&have_mng_structure);
6535 return((Image *) NULL);
6538 large_image=SyncNextImageInList(image);
6540 large_image->columns=magnified_width;
6541 large_image->rows=magnified_height;
6543 magn_methx=mng_info->magn_methx;
6544 magn_methy=mng_info->magn_methy;
6546 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6547 #define QM unsigned short
6548 if (magn_methx != 1 || magn_methy != 1)
6551 Scale pixels to unsigned shorts to prevent
6552 overflow of intermediate values of interpolations
6554 for (y=0; y < (ssize_t) image->rows; y++)
6556 q=GetAuthenticPixels(image,0,y,image->columns,1,
6559 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6561 SetPixelRed(image,ScaleQuantumToShort(
6562 GetPixelRed(image,q)),q);
6563 SetPixelGreen(image,ScaleQuantumToShort(
6564 GetPixelGreen(image,q)),q);
6565 SetPixelBlue(image,ScaleQuantumToShort(
6566 GetPixelBlue(image,q)),q);
6567 SetPixelAlpha(image,ScaleQuantumToShort(
6568 GetPixelAlpha(image,q)),q);
6569 q+=GetPixelChannels(image);
6572 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6580 if (image->alpha_trait != UndefinedPixelTrait)
6581 (void) SetImageBackgroundColor(large_image,exception);
6585 large_image->background_color.alpha=OpaqueAlpha;
6586 (void) SetImageBackgroundColor(large_image,exception);
6588 if (magn_methx == 4)
6591 if (magn_methx == 5)
6594 if (magn_methy == 4)
6597 if (magn_methy == 5)
6601 /* magnify the rows into the right side of the large image */
6603 if (logging != MagickFalse)
6604 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6605 " Magnify the rows to %.20g",(double) large_image->rows);
6606 m=(ssize_t) mng_info->magn_mt;
6608 length=(size_t) GetPixelChannels(image)*image->columns;
6609 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6610 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6612 if ((prev == (Quantum *) NULL) ||
6613 (next == (Quantum *) NULL))
6615 image=DestroyImageList(image);
6616 MngInfoFreeStruct(mng_info,&have_mng_structure);
6617 ThrowReaderException(ResourceLimitError,
6618 "MemoryAllocationFailed");
6621 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6622 (void) CopyMagickMemory(next,n,length);
6624 for (y=0; y < (ssize_t) image->rows; y++)
6627 m=(ssize_t) mng_info->magn_mt;
6629 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6630 m=(ssize_t) mng_info->magn_mb;
6632 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6633 m=(ssize_t) mng_info->magn_mb;
6635 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6639 m=(ssize_t) mng_info->magn_my;
6645 if (y < (ssize_t) image->rows-1)
6647 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6649 (void) CopyMagickMemory(next,n,length);
6652 for (i=0; i < m; i++, yy++)
6657 assert(yy < (ssize_t) large_image->rows);
6660 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6662 q+=(large_image->columns-image->columns)*
6663 GetPixelChannels(large_image);
6665 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6667 /* To do: get color as function of indexes[x] */
6669 if (image->storage_class == PseudoClass)
6674 if (magn_methy <= 1)
6676 /* replicate previous */
6677 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6678 SetPixelGreen(large_image,GetPixelGreen(image,
6680 SetPixelBlue(large_image,GetPixelBlue(image,
6682 SetPixelAlpha(large_image,GetPixelAlpha(image,
6686 else if (magn_methy == 2 || magn_methy == 4)
6690 SetPixelRed(large_image,GetPixelRed(image,
6692 SetPixelGreen(large_image,GetPixelGreen(image,
6694 SetPixelBlue(large_image,GetPixelBlue(image,
6696 SetPixelAlpha(large_image,GetPixelAlpha(image,
6703 SetPixelRed(large_image,((QM) (((ssize_t)
6704 (2*i*(GetPixelRed(image,n)
6705 -GetPixelRed(image,pixels)+m))/
6707 +GetPixelRed(image,pixels)))),q);
6708 SetPixelGreen(large_image,((QM) (((ssize_t)
6709 (2*i*(GetPixelGreen(image,n)
6710 -GetPixelGreen(image,pixels)+m))/
6712 +GetPixelGreen(image,pixels)))),q);
6713 SetPixelBlue(large_image,((QM) (((ssize_t)
6714 (2*i*(GetPixelBlue(image,n)
6715 -GetPixelBlue(image,pixels)+m))/
6717 +GetPixelBlue(image,pixels)))),q);
6719 if (image->alpha_trait != UndefinedPixelTrait)
6720 SetPixelAlpha(large_image, ((QM) (((ssize_t)
6721 (2*i*(GetPixelAlpha(image,n)
6722 -GetPixelAlpha(image,pixels)+m))
6724 GetPixelAlpha(image,pixels)))),q);
6727 if (magn_methy == 4)
6729 /* Replicate nearest */
6730 if (i <= ((m+1) << 1))
6731 SetPixelAlpha(large_image,GetPixelAlpha(image,
6734 SetPixelAlpha(large_image,GetPixelAlpha(image,
6739 else /* if (magn_methy == 3 || magn_methy == 5) */
6741 /* Replicate nearest */
6742 if (i <= ((m+1) << 1))
6744 SetPixelRed(large_image,GetPixelRed(image,
6746 SetPixelGreen(large_image,GetPixelGreen(image,
6748 SetPixelBlue(large_image,GetPixelBlue(image,
6750 SetPixelAlpha(large_image,GetPixelAlpha(image,
6756 SetPixelRed(large_image,GetPixelRed(image,n),q);
6757 SetPixelGreen(large_image,GetPixelGreen(image,n),
6759 SetPixelBlue(large_image,GetPixelBlue(image,n),
6761 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6765 if (magn_methy == 5)
6767 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6768 (GetPixelAlpha(image,n)
6769 -GetPixelAlpha(image,pixels))
6770 +m))/((ssize_t) (m*2))
6771 +GetPixelAlpha(image,pixels)),q);
6774 n+=GetPixelChannels(image);
6775 q+=GetPixelChannels(large_image);
6776 pixels+=GetPixelChannels(image);
6779 if (SyncAuthenticPixels(large_image,exception) == 0)
6785 prev=(Quantum *) RelinquishMagickMemory(prev);
6786 next=(Quantum *) RelinquishMagickMemory(next);
6788 length=image->columns;
6790 if (logging != MagickFalse)
6791 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6792 " Delete original image");
6794 DeleteImageFromList(&image);
6798 mng_info->image=image;
6800 /* magnify the columns */
6801 if (logging != MagickFalse)
6802 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6803 " Magnify the columns to %.20g",(double) image->columns);
6805 for (y=0; y < (ssize_t) image->rows; y++)
6810 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6811 pixels=q+(image->columns-length)*GetPixelChannels(image);
6812 n=pixels+GetPixelChannels(image);
6814 for (x=(ssize_t) (image->columns-length);
6815 x < (ssize_t) image->columns; x++)
6817 /* To do: Rewrite using Get/Set***PixelChannel() */
6819 if (x == (ssize_t) (image->columns-length))
6820 m=(ssize_t) mng_info->magn_ml;
6822 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6823 m=(ssize_t) mng_info->magn_mr;
6825 else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6826 m=(ssize_t) mng_info->magn_mr;
6828 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6832 m=(ssize_t) mng_info->magn_mx;
6834 for (i=0; i < m; i++)
6836 if (magn_methx <= 1)
6838 /* replicate previous */
6839 SetPixelRed(image,GetPixelRed(image,pixels),q);
6840 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6841 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6842 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6845 else if (magn_methx == 2 || magn_methx == 4)
6849 SetPixelRed(image,GetPixelRed(image,pixels),q);
6850 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6851 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6852 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6855 /* To do: Rewrite using Get/Set***PixelChannel() */
6859 SetPixelRed(image,(QM) ((2*i*(
6860 GetPixelRed(image,n)
6861 -GetPixelRed(image,pixels))+m)
6863 GetPixelRed(image,pixels)),q);
6865 SetPixelGreen(image,(QM) ((2*i*(
6866 GetPixelGreen(image,n)
6867 -GetPixelGreen(image,pixels))+m)
6869 GetPixelGreen(image,pixels)),q);
6871 SetPixelBlue(image,(QM) ((2*i*(
6872 GetPixelBlue(image,n)
6873 -GetPixelBlue(image,pixels))+m)
6875 GetPixelBlue(image,pixels)),q);
6876 if (image->alpha_trait != UndefinedPixelTrait)
6877 SetPixelAlpha(image,(QM) ((2*i*(
6878 GetPixelAlpha(image,n)
6879 -GetPixelAlpha(image,pixels))+m)
6881 GetPixelAlpha(image,pixels)),q);
6884 if (magn_methx == 4)
6886 /* Replicate nearest */
6887 if (i <= ((m+1) << 1))
6889 SetPixelAlpha(image,
6890 GetPixelAlpha(image,pixels)+0,q);
6894 SetPixelAlpha(image,
6895 GetPixelAlpha(image,n)+0,q);
6900 else /* if (magn_methx == 3 || magn_methx == 5) */
6902 /* Replicate nearest */
6903 if (i <= ((m+1) << 1))
6905 SetPixelRed(image,GetPixelRed(image,pixels),q);
6906 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6907 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6908 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6913 SetPixelRed(image,GetPixelRed(image,n),q);
6914 SetPixelGreen(image,GetPixelGreen(image,n),q);
6915 SetPixelBlue(image,GetPixelBlue(image,n),q);
6916 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6919 if (magn_methx == 5)
6922 SetPixelAlpha(image,
6923 (QM) ((2*i*( GetPixelAlpha(image,n)
6924 -GetPixelAlpha(image,pixels))+m)/
6926 +GetPixelAlpha(image,pixels)),q);
6929 q+=GetPixelChannels(image);
6931 n+=GetPixelChannels(image);
6934 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6937 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6938 if (magn_methx != 1 || magn_methy != 1)
6941 Rescale pixels to Quantum
6943 for (y=0; y < (ssize_t) image->rows; y++)
6945 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6947 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6949 SetPixelRed(image,ScaleShortToQuantum(
6950 GetPixelRed(image,q)),q);
6951 SetPixelGreen(image,ScaleShortToQuantum(
6952 GetPixelGreen(image,q)),q);
6953 SetPixelBlue(image,ScaleShortToQuantum(
6954 GetPixelBlue(image,q)),q);
6955 SetPixelAlpha(image,ScaleShortToQuantum(
6956 GetPixelAlpha(image,q)),q);
6957 q+=GetPixelChannels(image);
6960 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6965 if (logging != MagickFalse)
6966 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6967 " Finished MAGN processing");
6972 Crop_box is with respect to the upper left corner of the MNG.
6974 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6975 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6976 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6977 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6978 crop_box=mng_minimum_box(crop_box,mng_info->clip);
6979 crop_box=mng_minimum_box(crop_box,mng_info->frame);
6980 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6981 if ((crop_box.left != (mng_info->image_box.left
6982 +mng_info->x_off[object_id])) ||
6983 (crop_box.right != (mng_info->image_box.right
6984 +mng_info->x_off[object_id])) ||
6985 (crop_box.top != (mng_info->image_box.top
6986 +mng_info->y_off[object_id])) ||
6987 (crop_box.bottom != (mng_info->image_box.bottom
6988 +mng_info->y_off[object_id])))
6990 if (logging != MagickFalse)
6991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6992 " Crop the PNG image");
6994 if ((crop_box.left < crop_box.right) &&
6995 (crop_box.top < crop_box.bottom))
7004 Crop_info is with respect to the upper left corner of
7007 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7008 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7009 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7010 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7011 image->page.width=image->columns;
7012 image->page.height=image->rows;
7015 im=CropImage(image,&crop_info,exception);
7017 if (im != (Image *) NULL)
7019 image->columns=im->columns;
7020 image->rows=im->rows;
7021 im=DestroyImage(im);
7022 image->page.width=image->columns;
7023 image->page.height=image->rows;
7024 image->page.x=crop_box.left;
7025 image->page.y=crop_box.top;
7032 No pixels in crop area. The MNG spec still requires
7033 a layer, though, so make a single transparent pixel in
7034 the top left corner.
7039 (void) SetImageBackgroundColor(image,exception);
7040 image->page.width=1;
7041 image->page.height=1;
7046 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7047 image=mng_info->image;
7051 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7052 /* PNG does not handle depths greater than 16 so reduce it even
7055 if (image->depth > 16)
7059 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7060 if (image->depth > 8)
7062 /* To do: fill low byte properly */
7066 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7070 if (image_info->number_scenes != 0)
7072 if (mng_info->scenes_found >
7073 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7077 if (logging != MagickFalse)
7078 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7079 " Finished reading image datastream.");
7081 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7083 (void) CloseBlob(image);
7085 if (logging != MagickFalse)
7086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7087 " Finished reading all image datastreams.");
7089 #if defined(MNG_INSERT_LAYERS)
7090 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7091 (mng_info->mng_height))
7094 Insert a background layer if nothing else was found.
7096 if (logging != MagickFalse)
7097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7098 " No images found. Inserting a background layer.");
7100 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7103 Allocate next image structure.
7105 AcquireNextImage(image_info,image,exception);
7106 if (GetNextImageInList(image) == (Image *) NULL)
7108 image=DestroyImageList(image);
7109 MngInfoFreeStruct(mng_info,&have_mng_structure);
7111 if (logging != MagickFalse)
7112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7113 " Allocation failed, returning NULL.");
7115 return((Image *) NULL);
7117 image=SyncNextImageInList(image);
7119 image->columns=mng_info->mng_width;
7120 image->rows=mng_info->mng_height;
7121 image->page.width=mng_info->mng_width;
7122 image->page.height=mng_info->mng_height;
7125 image->background_color=mng_background_color;
7126 image->alpha_trait=UndefinedPixelTrait;
7128 if (image_info->ping == MagickFalse)
7129 (void) SetImageBackgroundColor(image,exception);
7131 mng_info->image_found++;
7134 image->iterations=mng_iterations;
7136 if (mng_iterations == 1)
7137 image->start_loop=MagickTrue;
7139 while (GetPreviousImageInList(image) != (Image *) NULL)
7142 if (image_count > 10*mng_info->image_found)
7144 if (logging != MagickFalse)
7145 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7147 (void) ThrowMagickException(exception,GetMagickModule(),
7148 CoderError,"Linked list is corrupted, beginning of list not found",
7149 "`%s'",image_info->filename);
7151 return((Image *) NULL);
7154 image=GetPreviousImageInList(image);
7156 if (GetNextImageInList(image) == (Image *) NULL)
7158 if (logging != MagickFalse)
7159 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7161 (void) ThrowMagickException(exception,GetMagickModule(),
7162 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7163 image_info->filename);
7167 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7168 GetNextImageInList(image) ==
7171 if (logging != MagickFalse)
7172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7173 " First image null");
7175 (void) ThrowMagickException(exception,GetMagickModule(),
7176 CoderError,"image->next for first image is NULL but shouldn't be.",
7177 "`%s'",image_info->filename);
7180 if (mng_info->image_found == 0)
7182 if (logging != MagickFalse)
7183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7184 " No visible images found.");
7186 (void) ThrowMagickException(exception,GetMagickModule(),
7187 CoderError,"No visible images in file","`%s'",image_info->filename);
7189 if (image != (Image *) NULL)
7190 image=DestroyImageList(image);
7192 MngInfoFreeStruct(mng_info,&have_mng_structure);
7193 return((Image *) NULL);
7196 if (mng_info->ticks_per_second)
7197 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7198 final_delay/mng_info->ticks_per_second;
7201 image->start_loop=MagickTrue;
7203 /* Find final nonzero image delay */
7204 final_image_delay=0;
7206 while (GetNextImageInList(image) != (Image *) NULL)
7209 final_image_delay=image->delay;
7211 image=GetNextImageInList(image);
7214 if (final_delay < final_image_delay)
7215 final_delay=final_image_delay;
7217 image->delay=final_delay;
7219 if (logging != MagickFalse)
7220 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7221 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7222 (double) final_delay);
7224 if (logging != MagickFalse)
7230 image=GetFirstImageInList(image);
7232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7233 " Before coalesce:");
7235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7236 " scene 0 delay=%.20g",(double) image->delay);
7238 while (GetNextImageInList(image) != (Image *) NULL)
7240 image=GetNextImageInList(image);
7241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7242 " scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7246 image=GetFirstImageInList(image);
7247 #ifdef MNG_COALESCE_LAYERS
7257 if (logging != MagickFalse)
7258 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Coalesce Images");
7261 next_image=CoalesceImages(image,exception);
7263 if (next_image == (Image *) NULL)
7264 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7266 image=DestroyImageList(image);
7269 for (next=image; next != (Image *) NULL; next=next_image)
7271 next->page.width=mng_info->mng_width;
7272 next->page.height=mng_info->mng_height;
7275 next->scene=scene++;
7276 next_image=GetNextImageInList(next);
7278 if (next_image == (Image *) NULL)
7281 if (next->delay == 0)
7284 next_image->previous=GetPreviousImageInList(next);
7285 if (GetPreviousImageInList(next) == (Image *) NULL)
7288 next->previous->next=next_image;
7289 next=DestroyImage(next);
7295 while (GetNextImageInList(image) != (Image *) NULL)
7296 image=GetNextImageInList(image);
7298 image->dispose=BackgroundDispose;
7300 if (logging != MagickFalse)
7306 image=GetFirstImageInList(image);
7308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7309 " After coalesce:");
7311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7312 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7313 (double) image->dispose);
7315 while (GetNextImageInList(image) != (Image *) NULL)
7317 image=GetNextImageInList(image);
7319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7320 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7321 (double) image->delay,(double) image->dispose);
7325 image=GetFirstImageInList(image);
7326 MngInfoFreeStruct(mng_info,&have_mng_structure);
7327 have_mng_structure=MagickFalse;
7329 if (logging != MagickFalse)
7330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7332 return(GetFirstImageInList(image));
7334 #else /* PNG_LIBPNG_VER > 10011 */
7335 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7337 printf("Your PNG library is too old: You have libpng-%s\n",
7338 PNG_LIBPNG_VER_STRING);
7340 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7341 "PNG library is too old","`%s'",image_info->filename);
7343 return(Image *) NULL;
7346 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7348 return(ReadPNGImage(image_info,exception));
7350 #endif /* PNG_LIBPNG_VER > 10011 */
7354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7358 % R e g i s t e r P N G I m a g e %
7362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7364 % RegisterPNGImage() adds properties for the PNG image format to
7365 % the list of supported formats. The properties include the image format
7366 % tag, a method to read and/or write the format, whether the format
7367 % supports the saving of more than one frame to the same file or blob,
7368 % whether the format supports native in-memory I/O, and a brief
7369 % description of the format.
7371 % The format of the RegisterPNGImage method is:
7373 % size_t RegisterPNGImage(void)
7376 ModuleExport size_t RegisterPNGImage(void)
7379 version[MagickPathExtent];
7387 "See http://www.libpng.org/ for details about the PNG format."
7392 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7398 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7404 #if defined(PNG_LIBPNG_VER_STRING)
7405 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7406 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MagickPathExtent);
7408 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7410 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7411 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7416 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7417 entry->flags|=CoderSeekableStreamFlag; /* To do: eliminate this. */
7419 #if defined(MAGICKCORE_PNG_DELEGATE)
7420 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7421 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7424 entry->magick=(IsImageFormatHandler *) IsMNG;
7426 if (*version != '\0')
7427 entry->version=ConstantString(version);
7429 entry->mime_type=ConstantString("video/x-mng");
7430 entry->note=ConstantString(MNGNote);
7431 (void) RegisterMagickInfo(entry);
7433 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7435 #if defined(MAGICKCORE_PNG_DELEGATE)
7436 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7437 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7440 entry->magick=(IsImageFormatHandler *) IsPNG;
7441 entry->flags^=CoderAdjoinFlag;
7442 entry->mime_type=ConstantString("image/png");
7444 if (*version != '\0')
7445 entry->version=ConstantString(version);
7447 entry->note=ConstantString(PNGNote);
7448 (void) RegisterMagickInfo(entry);
7450 entry=AcquireMagickInfo("PNG","PNG8",
7451 "8-bit indexed with optional binary transparency");
7453 #if defined(MAGICKCORE_PNG_DELEGATE)
7454 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7455 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7458 entry->magick=(IsImageFormatHandler *) IsPNG;
7459 entry->flags^=CoderAdjoinFlag;
7460 entry->mime_type=ConstantString("image/png");
7461 (void) RegisterMagickInfo(entry);
7463 entry=AcquireMagickInfo("PNG","PNG24",
7464 "opaque or binary transparent 24-bit RGB");
7467 #if defined(ZLIB_VERSION)
7468 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7469 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7471 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7473 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7474 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7478 if (*version != '\0')
7479 entry->version=ConstantString(version);
7481 #if defined(MAGICKCORE_PNG_DELEGATE)
7482 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7483 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7486 entry->magick=(IsImageFormatHandler *) IsPNG;
7487 entry->flags^=CoderAdjoinFlag;
7488 entry->mime_type=ConstantString("image/png");
7489 (void) RegisterMagickInfo(entry);
7491 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7493 #if defined(MAGICKCORE_PNG_DELEGATE)
7494 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7495 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7498 entry->magick=(IsImageFormatHandler *) IsPNG;
7499 entry->flags^=CoderAdjoinFlag;
7500 entry->mime_type=ConstantString("image/png");
7501 (void) RegisterMagickInfo(entry);
7503 entry=AcquireMagickInfo("PNG","PNG48",
7504 "opaque or binary transparent 48-bit RGB");
7506 #if defined(MAGICKCORE_PNG_DELEGATE)
7507 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7508 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7511 entry->magick=(IsImageFormatHandler *) IsPNG;
7512 entry->flags^=CoderAdjoinFlag;
7513 entry->mime_type=ConstantString("image/png");
7514 (void) RegisterMagickInfo(entry);
7516 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7518 #if defined(MAGICKCORE_PNG_DELEGATE)
7519 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7520 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7523 entry->magick=(IsImageFormatHandler *) IsPNG;
7524 entry->flags^=CoderAdjoinFlag;
7525 entry->mime_type=ConstantString("image/png");
7526 (void) RegisterMagickInfo(entry);
7528 entry=AcquireMagickInfo("PNG","PNG00",
7529 "PNG inheriting bit-depth, color-type from original, if possible");
7531 #if defined(MAGICKCORE_PNG_DELEGATE)
7532 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7533 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7536 entry->magick=(IsImageFormatHandler *) IsPNG;
7537 entry->flags^=CoderAdjoinFlag;
7538 entry->mime_type=ConstantString("image/png");
7539 (void) RegisterMagickInfo(entry);
7541 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7543 #if defined(JNG_SUPPORTED)
7544 #if defined(MAGICKCORE_PNG_DELEGATE)
7545 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7546 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7550 entry->magick=(IsImageFormatHandler *) IsJNG;
7551 entry->flags^=CoderAdjoinFlag;
7552 entry->mime_type=ConstantString("image/x-jng");
7553 entry->note=ConstantString(JNGNote);
7554 (void) RegisterMagickInfo(entry);
7556 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7557 ping_semaphore=AcquireSemaphoreInfo();
7560 return(MagickImageCoderSignature);
7564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7568 % U n r e g i s t e r P N G I m a g e %
7572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7574 % UnregisterPNGImage() removes format registrations made by the
7575 % PNG module from the list of supported formats.
7577 % The format of the UnregisterPNGImage method is:
7579 % UnregisterPNGImage(void)
7582 ModuleExport void UnregisterPNGImage(void)
7584 (void) UnregisterMagickInfo("MNG");
7585 (void) UnregisterMagickInfo("PNG");
7586 (void) UnregisterMagickInfo("PNG8");
7587 (void) UnregisterMagickInfo("PNG24");
7588 (void) UnregisterMagickInfo("PNG32");
7589 (void) UnregisterMagickInfo("PNG48");
7590 (void) UnregisterMagickInfo("PNG64");
7591 (void) UnregisterMagickInfo("PNG00");
7592 (void) UnregisterMagickInfo("JNG");
7594 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7595 if (ping_semaphore != (SemaphoreInfo *) NULL)
7596 RelinquishSemaphoreInfo(&ping_semaphore);
7600 #if defined(MAGICKCORE_PNG_DELEGATE)
7601 #if PNG_LIBPNG_VER > 10011
7603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7607 % W r i t e M N G I m a g e %
7611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7613 % WriteMNGImage() writes an image in the Portable Network Graphics
7614 % Group's "Multiple-image Network Graphics" encoded image format.
7616 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
7618 % The format of the WriteMNGImage method is:
7620 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7621 % Image *image,ExceptionInfo *exception)
7623 % A description of each parameter follows.
7625 % o image_info: the image info.
7627 % o image: The image.
7629 % o exception: return any errors or warnings in this structure.
7631 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7632 % "To do" under ReadPNGImage):
7634 % Preserve all unknown and not-yet-handled known chunks found in input
7635 % PNG file and copy them into output PNG files according to the PNG
7638 % Write the iCCP chunk at MNG level when (icc profile length > 0)
7640 % Improve selection of color type (use indexed-colour or indexed-colour
7641 % with tRNS when 256 or fewer unique RGBA values are present).
7643 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7644 % This will be complicated if we limit ourselves to generating MNG-LC
7645 % files. For now we ignore disposal method 3 and simply overlay the next
7648 % Check for identical PLTE's or PLTE/tRNS combinations and use a
7649 % global MNG PLTE or PLTE/tRNS combination when appropriate.
7650 % [mostly done 15 June 1999 but still need to take care of tRNS]
7652 % Check for identical sRGB and replace with a global sRGB (and remove
7653 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7654 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7655 % local gAMA/cHRM with local sRGB if appropriate).
7657 % Check for identical sBIT chunks and write global ones.
7659 % Provide option to skip writing the signature tEXt chunks.
7661 % Use signatures to detect identical objects and reuse the first
7662 % instance of such objects instead of writing duplicate objects.
7664 % Use a smaller-than-32k value of compression window size when
7667 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
7668 % ancillary text chunks and save profiles.
7670 % Provide an option to force LC files (to ensure exact framing rate)
7673 % Provide an option to force VLC files instead of LC, even when offsets
7674 % are present. This will involve expanding the embedded images with a
7675 % transparent region at the top and/or left.
7679 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7680 png_info *ping_info, unsigned char *profile_type, unsigned char
7681 *profile_description, unsigned char *profile_data, png_uint_32 length)
7700 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7702 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7705 if (image_info->verbose)
7707 (void) printf("writing raw profile: type=%s, length=%.20g\n",
7708 (char *) profile_type, (double) length);
7711 #if PNG_LIBPNG_VER >= 10400
7712 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7714 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7716 description_length=(png_uint_32) strlen((const char *) profile_description);
7717 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7718 + description_length);
7719 #if PNG_LIBPNG_VER >= 10400
7720 text[0].text=(png_charp) png_malloc(ping,
7721 (png_alloc_size_t) allocated_length);
7722 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7724 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7725 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7727 text[0].key[0]='\0';
7728 (void) ConcatenateMagickString(text[0].key,
7729 "Raw profile type ",MagickPathExtent);
7730 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7734 (void) CopyMagickString(dp,(const char *) profile_description,
7736 dp+=description_length;
7738 (void) FormatLocaleString(dp,allocated_length-
7739 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7742 for (i=0; i < (ssize_t) length; i++)
7746 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7747 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7752 text[0].text_length=(png_size_t) (dp-text[0].text);
7753 text[0].compression=image_info->compression == NoCompression ||
7754 (image_info->compression == UndefinedCompression &&
7755 text[0].text_length < 128) ? -1 : 0;
7757 if (text[0].text_length <= allocated_length)
7758 png_set_text(ping,ping_info,text,1);
7760 png_free(ping,text[0].text);
7761 png_free(ping,text[0].key);
7762 png_free(ping,text);
7765 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7766 const char *string, MagickBooleanType logging)
7779 ResetImageProfileIterator(image);
7781 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7783 profile=GetImageProfile(image,name);
7785 if (profile != (const StringInfo *) NULL)
7790 if (LocaleNCompare(name,string,11) == 0)
7792 if (logging != MagickFalse)
7793 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7794 " Found %s profile",name);
7796 ping_profile=CloneStringInfo(profile);
7797 data=GetStringInfoDatum(ping_profile),
7798 length=(png_uint_32) GetStringInfoLength(ping_profile);
7803 (void) WriteBlobMSBULong(image,length-5); /* data length */
7804 (void) WriteBlob(image,length-1,data+1);
7805 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7806 ping_profile=DestroyStringInfo(ping_profile);
7810 name=GetNextImageProfile(image);
7816 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
7817 const Quantum *p, const PixelInfo *q)
7822 value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
7823 if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
7824 return(MagickFalse);
7825 value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
7826 if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
7827 return(MagickFalse);
7828 value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
7829 if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
7830 return(MagickFalse);
7835 #if defined(PNG_tIME_SUPPORTED)
7836 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7837 const char *date,ExceptionInfo *exception)
7853 if (date != (const char *) NULL)
7855 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7858 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7859 "Invalid date format specified for png:tIME","`%s'",
7863 ptime.year=(png_uint_16) year;
7864 ptime.month=(png_byte) month;
7865 ptime.day=(png_byte) day;
7866 ptime.hour=(png_byte) hour;
7867 ptime.minute=(png_byte) minute;
7868 ptime.second=(png_byte) second;
7873 png_convert_from_time_t(&ptime,ttime);
7875 png_set_tIME(ping,info,&ptime);
7879 /* Write one PNG image */
7880 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7881 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7912 ping_trans_alpha[256];
7940 ping_have_cheap_transparency,
7953 /* ping_exclude_EXIF, */
7956 /* ping_exclude_iTXt, */
7962 /* ping_exclude_tRNS, */
7964 ping_exclude_zCCP, /* hex-encoded iCCP */
7967 ping_preserve_colormap,
7969 ping_need_colortype_warning,
7977 *volatile pixel_info;
7996 ping_interlace_method,
7997 ping_compression_method,
8014 number_semitransparent,
8016 ping_pHYs_unit_type;
8019 ping_pHYs_x_resolution,
8020 ping_pHYs_y_resolution;
8022 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8023 " Enter WriteOnePNGImage()");
8025 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8026 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8027 if (image_info == (ImageInfo *) NULL)
8028 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8030 /* Define these outside of the following "if logging()" block so they will
8031 * show in debuggers.
8034 (void) ConcatenateMagickString(im_vers,
8035 MagickLibVersionText,MagickPathExtent);
8036 (void) ConcatenateMagickString(im_vers,
8037 MagickLibAddendum,MagickPathExtent);
8040 (void) ConcatenateMagickString(libpng_vers,
8041 PNG_LIBPNG_VER_STRING,32);
8043 (void) ConcatenateMagickString(libpng_runv,
8044 png_get_libpng_ver(NULL),32);
8047 (void) ConcatenateMagickString(zlib_vers,
8050 (void) ConcatenateMagickString(zlib_runv,
8053 if (logging != MagickFalse)
8055 LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8057 LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8059 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8061 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8064 LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8066 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8068 LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8073 /* Initialize some stuff */
8076 ping_interlace_method=0,
8077 ping_compression_method=0,
8078 ping_filter_method=0,
8081 ping_background.red = 0;
8082 ping_background.green = 0;
8083 ping_background.blue = 0;
8084 ping_background.gray = 0;
8085 ping_background.index = 0;
8087 ping_trans_color.red=0;
8088 ping_trans_color.green=0;
8089 ping_trans_color.blue=0;
8090 ping_trans_color.gray=0;
8092 ping_pHYs_unit_type = 0;
8093 ping_pHYs_x_resolution = 0;
8094 ping_pHYs_y_resolution = 0;
8096 ping_have_blob=MagickFalse;
8097 ping_have_cheap_transparency=MagickFalse;
8098 ping_have_color=MagickTrue;
8099 ping_have_non_bw=MagickTrue;
8100 ping_have_PLTE=MagickFalse;
8101 ping_have_bKGD=MagickFalse;
8102 ping_have_iCCP=MagickFalse;
8103 ping_have_pHYs=MagickFalse;
8104 ping_have_sRGB=MagickFalse;
8105 ping_have_tRNS=MagickFalse;
8107 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8108 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8109 ping_exclude_date=mng_info->ping_exclude_date;
8110 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8111 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8112 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8113 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8114 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8115 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8116 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8117 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8118 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8119 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8120 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8121 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8122 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8124 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8125 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8126 ping_need_colortype_warning = MagickFalse;
8128 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8129 * i.e., eliminate the ICC profile and set image->rendering_intent.
8130 * Note that this will not involve any changes to the actual pixels
8131 * but merely passes information to applications that read the resulting
8134 * To do: recognize other variants of the sRGB profile, using the CRC to
8135 * verify all recognized variants including the 7 already known.
8137 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8139 * Use something other than image->rendering_intent to record the fact
8140 * that the sRGB profile was found.
8142 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8143 * profile. Record the Blackpoint Compensation, if any.
8145 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8153 ResetImageProfileIterator(image);
8154 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8156 profile=GetImageProfile(image,name);
8158 if (profile != (StringInfo *) NULL)
8160 if ((LocaleCompare(name,"ICC") == 0) ||
8161 (LocaleCompare(name,"ICM") == 0))
8176 length=(png_uint_32) GetStringInfoLength(profile);
8178 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8180 if (length == sRGB_info[icheck].len)
8184 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8185 " Got a %lu-byte ICC profile (potentially sRGB)",
8186 (unsigned long) length);
8188 data=GetStringInfoDatum(profile);
8189 profile_crc=crc32(0,data,length);
8191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192 " with crc=%8x",(unsigned int) profile_crc);
8196 if (profile_crc == sRGB_info[icheck].crc)
8198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199 " It is sRGB with rendering intent = %s",
8200 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8201 sRGB_info[icheck].intent));
8202 if (image->rendering_intent==UndefinedIntent)
8204 image->rendering_intent=
8205 Magick_RenderingIntent_from_PNG_RenderingIntent(
8206 sRGB_info[icheck].intent);
8208 ping_exclude_iCCP = MagickTrue;
8209 ping_exclude_zCCP = MagickTrue;
8210 ping_have_sRGB = MagickTrue;
8215 if (sRGB_info[icheck].len == 0)
8216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8217 " Got a %lu-byte ICC profile not recognized as sRGB",
8218 (unsigned long) length);
8221 name=GetNextImageProfile(image);
8226 number_semitransparent = 0;
8227 number_transparent = 0;
8229 if (logging != MagickFalse)
8231 if (image->storage_class == UndefinedClass)
8232 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233 " image->storage_class=UndefinedClass");
8234 if (image->storage_class == DirectClass)
8235 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8236 " image->storage_class=DirectClass");
8237 if (image->storage_class == PseudoClass)
8238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239 " image->storage_class=PseudoClass");
8240 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8241 " image->taint=MagickTrue":
8242 " image->taint=MagickFalse");
8245 if (image->storage_class == PseudoClass &&
8246 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8247 mng_info->write_png48 || mng_info->write_png64 ||
8248 (mng_info->write_png_colortype != 1 &&
8249 mng_info->write_png_colortype != 5)))
8251 (void) SyncImage(image,exception);
8252 image->storage_class = DirectClass;
8255 if (ping_preserve_colormap == MagickFalse)
8257 if (image->storage_class != PseudoClass && image->colormap != NULL)
8259 /* Free the bogus colormap; it can cause trouble later */
8260 if (logging != MagickFalse)
8261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8262 " Freeing bogus colormap");
8263 (void) RelinquishMagickMemory(image->colormap);
8264 image->colormap=NULL;
8268 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8269 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8272 Sometimes we get PseudoClass images whose RGB values don't match
8273 the colors in the colormap. This code syncs the RGB values.
8275 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8276 (void) SyncImage(image,exception);
8278 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8279 if (image->depth > 8)
8281 if (logging != MagickFalse)
8282 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8283 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8289 /* Respect the -depth option */
8290 if (image->depth < 4)
8295 if (image->depth > 2)
8297 /* Scale to 4-bit */
8298 LBR04PacketRGBO(image->background_color);
8300 for (y=0; y < (ssize_t) image->rows; y++)
8302 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8304 if (r == (Quantum *) NULL)
8307 for (x=0; x < (ssize_t) image->columns; x++)
8310 r+=GetPixelChannels(image);
8313 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8317 if (image->storage_class == PseudoClass && image->colormap != NULL)
8319 for (i=0; i < (ssize_t) image->colors; i++)
8321 LBR04PacketRGBO(image->colormap[i]);
8325 else if (image->depth > 1)
8327 /* Scale to 2-bit */
8328 LBR02PacketRGBO(image->background_color);
8330 for (y=0; y < (ssize_t) image->rows; y++)
8332 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8334 if (r == (Quantum *) NULL)
8337 for (x=0; x < (ssize_t) image->columns; x++)
8340 r+=GetPixelChannels(image);
8343 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8347 if (image->storage_class == PseudoClass && image->colormap != NULL)
8349 for (i=0; i < (ssize_t) image->colors; i++)
8351 LBR02PacketRGBO(image->colormap[i]);
8357 /* Scale to 1-bit */
8358 LBR01PacketRGBO(image->background_color);
8360 for (y=0; y < (ssize_t) image->rows; y++)
8362 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8364 if (r == (Quantum *) NULL)
8367 for (x=0; x < (ssize_t) image->columns; x++)
8370 r+=GetPixelChannels(image);
8373 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8377 if (image->storage_class == PseudoClass && image->colormap != NULL)
8379 for (i=0; i < (ssize_t) image->colors; i++)
8381 LBR01PacketRGBO(image->colormap[i]);
8387 /* To do: set to next higher multiple of 8 */
8388 if (image->depth < 8)
8391 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8392 /* PNG does not handle depths greater than 16 so reduce it even
8395 if (image->depth > 8)
8399 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8400 if (image->depth > 8)
8402 /* To do: fill low byte properly */
8406 if (image->depth == 16 && mng_info->write_png_depth != 16)
8407 if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8411 image_colors = (int) image->colors;
8412 number_opaque = (int) image->colors;
8413 number_transparent = 0;
8414 number_semitransparent = 0;
8416 if (mng_info->write_png_colortype &&
8417 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8418 mng_info->write_png_colortype < 4 &&
8419 image->alpha_trait == UndefinedPixelTrait)))
8421 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8422 * are not going to need the result.
8424 if (mng_info->write_png_colortype == 1 ||
8425 mng_info->write_png_colortype == 5)
8426 ping_have_color=MagickFalse;
8428 if (image->alpha_trait != UndefinedPixelTrait)
8430 number_transparent = 2;
8431 number_semitransparent = 1;
8435 if (mng_info->write_png_colortype < 7)
8439 * Normally we run this just once, but in the case of writing PNG8
8440 * we reduce the transparency to binary and run again, then if there
8441 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8442 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8443 * palette. Then (To do) we take care of a final reduction that is only
8444 * needed if there are still 256 colors present and one of them has both
8445 * transparent and opaque instances.
8448 tried_332 = MagickFalse;
8449 tried_333 = MagickFalse;
8450 tried_444 = MagickFalse;
8455 * Sometimes we get DirectClass images that have 256 colors or fewer.
8456 * This code will build a colormap.
8458 * Also, sometimes we get PseudoClass images with an out-of-date
8459 * colormap. This code will replace the colormap with a new one.
8460 * Sometimes we get PseudoClass images that have more than 256 colors.
8461 * This code will delete the colormap and change the image to
8464 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8465 * even though it sometimes contains left-over non-opaque values.
8467 * Also we gather some information (number of opaque, transparent,
8468 * and semitransparent pixels, and whether the image has any non-gray
8469 * pixels or only black-and-white pixels) that we might need later.
8471 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8472 * we need to check for bogus non-opaque values, at least.
8480 semitransparent[260],
8483 register const Quantum
8490 if (logging != MagickFalse)
8491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8492 " Enter BUILD_PALETTE:");
8494 if (logging != MagickFalse)
8496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8497 " image->columns=%.20g",(double) image->columns);
8498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8499 " image->rows=%.20g",(double) image->rows);
8500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8501 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8502 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8503 " image->depth=%.20g",(double) image->depth);
8505 if (image->storage_class == PseudoClass && image->colormap != NULL)
8507 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8508 " Original colormap:");
8509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8510 " i (red,green,blue,alpha)");
8512 for (i=0; i < 256; i++)
8514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8515 " %d (%d,%d,%d,%d)",
8517 (int) image->colormap[i].red,
8518 (int) image->colormap[i].green,
8519 (int) image->colormap[i].blue,
8520 (int) image->colormap[i].alpha);
8523 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8527 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8528 " %d (%d,%d,%d,%d)",
8530 (int) image->colormap[i].red,
8531 (int) image->colormap[i].green,
8532 (int) image->colormap[i].blue,
8533 (int) image->colormap[i].alpha);
8538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8539 " image->colors=%d",(int) image->colors);
8541 if (image->colors == 0)
8542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8543 " (zero means unknown)");
8545 if (ping_preserve_colormap == MagickFalse)
8546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8547 " Regenerate the colormap");
8552 number_semitransparent = 0;
8553 number_transparent = 0;
8555 for (y=0; y < (ssize_t) image->rows; y++)
8557 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8559 if (q == (Quantum *) NULL)
8562 for (x=0; x < (ssize_t) image->columns; x++)
8564 if (image->alpha_trait == UndefinedPixelTrait ||
8565 GetPixelAlpha(image,q) == OpaqueAlpha)
8567 if (number_opaque < 259)
8569 if (number_opaque == 0)
8571 GetPixelInfoPixel(image, q, opaque);
8572 opaque[0].alpha=OpaqueAlpha;
8576 for (i=0; i< (ssize_t) number_opaque; i++)
8578 if (Magick_png_color_equal(image,q,opaque+i))
8582 if (i == (ssize_t) number_opaque && number_opaque < 259)
8585 GetPixelInfoPixel(image, q, opaque+i);
8586 opaque[i].alpha=OpaqueAlpha;
8590 else if (GetPixelAlpha(image,q) == TransparentAlpha)
8592 if (number_transparent < 259)
8594 if (number_transparent == 0)
8596 GetPixelInfoPixel(image, q, transparent);
8597 ping_trans_color.red=(unsigned short)
8598 GetPixelRed(image,q);
8599 ping_trans_color.green=(unsigned short)
8600 GetPixelGreen(image,q);
8601 ping_trans_color.blue=(unsigned short)
8602 GetPixelBlue(image,q);
8603 ping_trans_color.gray=(unsigned short)
8604 GetPixelGray(image,q);
8605 number_transparent = 1;
8608 for (i=0; i< (ssize_t) number_transparent; i++)
8610 if (Magick_png_color_equal(image,q,transparent+i))
8614 if (i == (ssize_t) number_transparent &&
8615 number_transparent < 259)
8617 number_transparent++;
8618 GetPixelInfoPixel(image,q,transparent+i);
8624 if (number_semitransparent < 259)
8626 if (number_semitransparent == 0)
8628 GetPixelInfoPixel(image,q,semitransparent);
8629 number_semitransparent = 1;
8632 for (i=0; i< (ssize_t) number_semitransparent; i++)
8634 if (Magick_png_color_equal(image,q,semitransparent+i)
8635 && GetPixelAlpha(image,q) ==
8636 semitransparent[i].alpha)
8640 if (i == (ssize_t) number_semitransparent &&
8641 number_semitransparent < 259)
8643 number_semitransparent++;
8644 GetPixelInfoPixel(image, q, semitransparent+i);
8648 q+=GetPixelChannels(image);
8652 if (mng_info->write_png8 == MagickFalse &&
8653 ping_exclude_bKGD == MagickFalse)
8655 /* Add the background color to the palette, if it
8656 * isn't already there.
8658 if (logging != MagickFalse)
8660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8661 " Check colormap for background (%d,%d,%d)",
8662 (int) image->background_color.red,
8663 (int) image->background_color.green,
8664 (int) image->background_color.blue);
8666 for (i=0; i<number_opaque; i++)
8668 if (opaque[i].red == image->background_color.red &&
8669 opaque[i].green == image->background_color.green &&
8670 opaque[i].blue == image->background_color.blue)
8673 if (number_opaque < 259 && i == number_opaque)
8675 opaque[i] = image->background_color;
8676 ping_background.index = i;
8678 if (logging != MagickFalse)
8680 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8681 " background_color index is %d",(int) i);
8685 else if (logging != MagickFalse)
8686 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8687 " No room in the colormap to add background color");
8690 image_colors=number_opaque+number_transparent+number_semitransparent;
8692 if (logging != MagickFalse)
8694 if (image_colors > 256)
8695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8696 " image has more than 256 colors");
8699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8700 " image has %d colors",image_colors);
8703 if (ping_preserve_colormap != MagickFalse)
8706 if (mng_info->write_png_colortype != 7) /* We won't need this info */
8708 ping_have_color=MagickFalse;
8709 ping_have_non_bw=MagickFalse;
8711 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8714 "incompatible colorspace");
8715 ping_have_color=MagickTrue;
8716 ping_have_non_bw=MagickTrue;
8719 if(image_colors > 256)
8721 for (y=0; y < (ssize_t) image->rows; y++)
8723 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8725 if (q == (Quantum *) NULL)
8729 for (x=0; x < (ssize_t) image->columns; x++)
8731 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8732 GetPixelRed(image,s) != GetPixelBlue(image,s))
8734 ping_have_color=MagickTrue;
8735 ping_have_non_bw=MagickTrue;
8738 s+=GetPixelChannels(image);
8741 if (ping_have_color != MagickFalse)
8744 /* Worst case is black-and-white; we are looking at every
8748 if (ping_have_non_bw == MagickFalse)
8751 for (x=0; x < (ssize_t) image->columns; x++)
8753 if (GetPixelRed(image,s) != 0 &&
8754 GetPixelRed(image,s) != QuantumRange)
8756 ping_have_non_bw=MagickTrue;
8759 s+=GetPixelChannels(image);
8766 if (image_colors < 257)
8772 * Initialize image colormap.
8775 if (logging != MagickFalse)
8776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8777 " Sort the new colormap");
8779 /* Sort palette, transparent first */;
8783 for (i=0; i<number_transparent; i++)
8784 colormap[n++] = transparent[i];
8786 for (i=0; i<number_semitransparent; i++)
8787 colormap[n++] = semitransparent[i];
8789 for (i=0; i<number_opaque; i++)
8790 colormap[n++] = opaque[i];
8792 ping_background.index +=
8793 (number_transparent + number_semitransparent);
8795 /* image_colors < 257; search the colormap instead of the pixels
8796 * to get ping_have_color and ping_have_non_bw
8800 if (ping_have_color == MagickFalse)
8802 if (colormap[i].red != colormap[i].green ||
8803 colormap[i].red != colormap[i].blue)
8805 ping_have_color=MagickTrue;
8806 ping_have_non_bw=MagickTrue;
8811 if (ping_have_non_bw == MagickFalse)
8813 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8814 ping_have_non_bw=MagickTrue;
8818 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8819 (number_transparent == 0 && number_semitransparent == 0)) &&
8820 (((mng_info->write_png_colortype-1) ==
8821 PNG_COLOR_TYPE_PALETTE) ||
8822 (mng_info->write_png_colortype == 0)))
8824 if (logging != MagickFalse)
8826 if (n != (ssize_t) image_colors)
8827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8828 " image_colors (%d) and n (%d) don't match",
8831 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8832 " AcquireImageColormap");
8835 image->colors = image_colors;
8837 if (AcquireImageColormap(image,image_colors,exception) ==
8839 ThrowWriterException(ResourceLimitError,
8840 "MemoryAllocationFailed");
8842 for (i=0; i< (ssize_t) image_colors; i++)
8843 image->colormap[i] = colormap[i];
8845 if (logging != MagickFalse)
8847 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8848 " image->colors=%d (%d)",
8849 (int) image->colors, image_colors);
8851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852 " Update the pixel indexes");
8855 /* Sync the pixel indices with the new colormap */
8857 for (y=0; y < (ssize_t) image->rows; y++)
8859 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8861 if (q == (Quantum *) NULL)
8864 for (x=0; x < (ssize_t) image->columns; x++)
8866 for (i=0; i< (ssize_t) image_colors; i++)
8868 if ((image->alpha_trait == UndefinedPixelTrait ||
8869 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8870 image->colormap[i].red == GetPixelRed(image,q) &&
8871 image->colormap[i].green == GetPixelGreen(image,q) &&
8872 image->colormap[i].blue == GetPixelBlue(image,q))
8874 SetPixelIndex(image,i,q);
8878 q+=GetPixelChannels(image);
8881 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8887 if (logging != MagickFalse)
8889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8890 " image->colors=%d", (int) image->colors);
8892 if (image->colormap != NULL)
8894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8895 " i (red,green,blue,alpha)");
8897 for (i=0; i < (ssize_t) image->colors; i++)
8899 if (i < 300 || i >= (ssize_t) image->colors - 10)
8901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902 " %d (%d,%d,%d,%d)",
8904 (int) image->colormap[i].red,
8905 (int) image->colormap[i].green,
8906 (int) image->colormap[i].blue,
8907 (int) image->colormap[i].alpha);
8912 if (number_transparent < 257)
8913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8914 " number_transparent = %d",
8915 number_transparent);
8918 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8919 " number_transparent > 256");
8921 if (number_opaque < 257)
8922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8923 " number_opaque = %d",
8927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8928 " number_opaque > 256");
8930 if (number_semitransparent < 257)
8931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8932 " number_semitransparent = %d",
8933 number_semitransparent);
8936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8937 " number_semitransparent > 256");
8939 if (ping_have_non_bw == MagickFalse)
8940 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8941 " All pixels and the background are black or white");
8943 else if (ping_have_color == MagickFalse)
8944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8945 " All pixels and the background are gray");
8948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949 " At least one pixel or the background is non-gray");
8951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8952 " Exit BUILD_PALETTE:");
8955 if (mng_info->write_png8 == MagickFalse)
8958 /* Make any reductions necessary for the PNG8 format */
8959 if (image_colors <= 256 &&
8960 image_colors != 0 && image->colormap != NULL &&
8961 number_semitransparent == 0 &&
8962 number_transparent <= 1)
8965 /* PNG8 can't have semitransparent colors so we threshold the
8966 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8967 * transparent color so if more than one is transparent we merge
8968 * them into image->background_color.
8970 if (number_semitransparent != 0 || number_transparent > 1)
8972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8973 " Thresholding the alpha channel to binary");
8975 for (y=0; y < (ssize_t) image->rows; y++)
8977 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8979 if (r == (Quantum *) NULL)
8982 for (x=0; x < (ssize_t) image->columns; x++)
8984 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8986 SetPixelViaPixelInfo(image,&image->background_color,r);
8987 SetPixelAlpha(image,TransparentAlpha,r);
8990 SetPixelAlpha(image,OpaqueAlpha,r);
8991 r+=GetPixelChannels(image);
8994 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8997 if (image_colors != 0 && image_colors <= 256 &&
8998 image->colormap != NULL)
8999 for (i=0; i<image_colors; i++)
9000 image->colormap[i].alpha =
9001 (image->colormap[i].alpha > TransparentAlpha/2 ?
9002 TransparentAlpha : OpaqueAlpha);
9007 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9008 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9009 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9012 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9014 if (logging != MagickFalse)
9015 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016 " Quantizing the background color to 4-4-4");
9018 tried_444 = MagickTrue;
9020 LBR04PacketRGB(image->background_color);
9022 if (logging != MagickFalse)
9023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9024 " Quantizing the pixel colors to 4-4-4");
9026 if (image->colormap == NULL)
9028 for (y=0; y < (ssize_t) image->rows; y++)
9030 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9032 if (r == (Quantum *) NULL)
9035 for (x=0; x < (ssize_t) image->columns; x++)
9037 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9039 r+=GetPixelChannels(image);
9042 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9047 else /* Should not reach this; colormap already exists and
9050 if (logging != MagickFalse)
9051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052 " Quantizing the colormap to 4-4-4");
9054 for (i=0; i<image_colors; i++)
9056 LBR04PacketRGB(image->colormap[i]);
9062 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9064 if (logging != MagickFalse)
9065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9066 " Quantizing the background color to 3-3-3");
9068 tried_333 = MagickTrue;
9070 LBR03PacketRGB(image->background_color);
9072 if (logging != MagickFalse)
9073 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9074 " Quantizing the pixel colors to 3-3-3-1");
9076 if (image->colormap == NULL)
9078 for (y=0; y < (ssize_t) image->rows; y++)
9080 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9082 if (r == (Quantum *) NULL)
9085 for (x=0; x < (ssize_t) image->columns; x++)
9087 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9089 r+=GetPixelChannels(image);
9092 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9097 else /* Should not reach this; colormap already exists and
9100 if (logging != MagickFalse)
9101 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9102 " Quantizing the colormap to 3-3-3-1");
9103 for (i=0; i<image_colors; i++)
9105 LBR03PacketRGB(image->colormap[i]);
9111 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9113 if (logging != MagickFalse)
9114 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115 " Quantizing the background color to 3-3-2");
9117 tried_332 = MagickTrue;
9119 /* Red and green were already done so we only quantize the blue
9123 LBR02PacketBlue(image->background_color);
9125 if (logging != MagickFalse)
9126 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9127 " Quantizing the pixel colors to 3-3-2-1");
9129 if (image->colormap == NULL)
9131 for (y=0; y < (ssize_t) image->rows; y++)
9133 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9135 if (r == (Quantum *) NULL)
9138 for (x=0; x < (ssize_t) image->columns; x++)
9140 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9142 r+=GetPixelChannels(image);
9145 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9150 else /* Should not reach this; colormap already exists and
9153 if (logging != MagickFalse)
9154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155 " Quantizing the colormap to 3-3-2-1");
9156 for (i=0; i<image_colors; i++)
9158 LBR02PacketBlue(image->colormap[i]);
9164 if (image_colors == 0 || image_colors > 256)
9166 /* Take care of special case with 256 opaque colors + 1 transparent
9167 * color. We don't need to quantize to 2-3-2-1; we only need to
9168 * eliminate one color, so we'll merge the two darkest red
9169 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9171 if (logging != MagickFalse)
9172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9173 " Merging two dark red background colors to 3-3-2-1");
9175 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9176 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9177 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9179 image->background_color.red=ScaleCharToQuantum(0x24);
9182 if (logging != MagickFalse)
9183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9184 " Merging two dark red pixel colors to 3-3-2-1");
9186 if (image->colormap == NULL)
9188 for (y=0; y < (ssize_t) image->rows; y++)
9190 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9192 if (r == (Quantum *) NULL)
9195 for (x=0; x < (ssize_t) image->columns; x++)
9197 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9198 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9199 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9200 GetPixelAlpha(image,r) == OpaqueAlpha)
9202 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9204 r+=GetPixelChannels(image);
9207 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9215 for (i=0; i<image_colors; i++)
9217 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9218 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9219 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9221 image->colormap[i].red=ScaleCharToQuantum(0x24);
9228 /* END OF BUILD_PALETTE */
9230 /* If we are excluding the tRNS chunk and there is transparency,
9231 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9234 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9235 (number_transparent != 0 || number_semitransparent != 0))
9237 unsigned int colortype=mng_info->write_png_colortype;
9239 if (ping_have_color == MagickFalse)
9240 mng_info->write_png_colortype = 5;
9243 mng_info->write_png_colortype = 7;
9245 if (colortype != 0 &&
9246 mng_info->write_png_colortype != colortype)
9247 ping_need_colortype_warning=MagickTrue;
9251 /* See if cheap transparency is possible. It is only possible
9252 * when there is a single transparent color, no semitransparent
9253 * color, and no opaque color that has the same RGB components
9254 * as the transparent color. We only need this information if
9255 * we are writing a PNG with colortype 0 or 2, and we have not
9256 * excluded the tRNS chunk.
9258 if (number_transparent == 1 &&
9259 mng_info->write_png_colortype < 4)
9261 ping_have_cheap_transparency = MagickTrue;
9263 if (number_semitransparent != 0)
9264 ping_have_cheap_transparency = MagickFalse;
9266 else if (image_colors == 0 || image_colors > 256 ||
9267 image->colormap == NULL)
9269 register const Quantum
9272 for (y=0; y < (ssize_t) image->rows; y++)
9274 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9276 if (q == (Quantum *) NULL)
9279 for (x=0; x < (ssize_t) image->columns; x++)
9281 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9282 (unsigned short) GetPixelRed(image,q) ==
9283 ping_trans_color.red &&
9284 (unsigned short) GetPixelGreen(image,q) ==
9285 ping_trans_color.green &&
9286 (unsigned short) GetPixelBlue(image,q) ==
9287 ping_trans_color.blue)
9289 ping_have_cheap_transparency = MagickFalse;
9293 q+=GetPixelChannels(image);
9296 if (ping_have_cheap_transparency == MagickFalse)
9302 /* Assuming that image->colormap[0] is the one transparent color
9303 * and that all others are opaque.
9305 if (image_colors > 1)
9306 for (i=1; i<image_colors; i++)
9307 if (image->colormap[i].red == image->colormap[0].red &&
9308 image->colormap[i].green == image->colormap[0].green &&
9309 image->colormap[i].blue == image->colormap[0].blue)
9311 ping_have_cheap_transparency = MagickFalse;
9316 if (logging != MagickFalse)
9318 if (ping_have_cheap_transparency == MagickFalse)
9319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9320 " Cheap transparency is not possible.");
9323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9324 " Cheap transparency is possible.");
9328 ping_have_cheap_transparency = MagickFalse;
9330 image_depth=image->depth;
9332 quantum_info = (QuantumInfo *) NULL;
9334 image_colors=(int) image->colors;
9335 image_matte=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse;
9337 if (mng_info->write_png_colortype < 5)
9338 mng_info->IsPalette=image->storage_class == PseudoClass &&
9339 image_colors <= 256 && image->colormap != NULL;
9341 mng_info->IsPalette = MagickFalse;
9343 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9344 (image->colors == 0 || image->colormap == NULL))
9346 image_info=DestroyImageInfo(image_info);
9347 image=DestroyImage(image);
9348 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9349 "Cannot write PNG8 or color-type 3; colormap is NULL",
9350 "`%s'",IMimage->filename);
9351 return(MagickFalse);
9355 Allocate the PNG structures
9357 #ifdef PNG_USER_MEM_SUPPORTED
9358 error_info.image=image;
9359 error_info.exception=exception;
9360 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9361 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9362 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9365 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9366 MagickPNGErrorHandler,MagickPNGWarningHandler);
9369 if (ping == (png_struct *) NULL)
9370 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9372 ping_info=png_create_info_struct(ping);
9374 if (ping_info == (png_info *) NULL)
9376 png_destroy_write_struct(&ping,(png_info **) NULL);
9377 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9380 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9381 pixel_info=(MemoryInfo *) NULL;
9383 if (setjmp(png_jmpbuf(ping)))
9389 if (image_info->verbose)
9390 (void) printf("PNG write has failed.\n");
9392 png_destroy_write_struct(&ping,&ping_info);
9393 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9394 UnlockSemaphoreInfo(ping_semaphore);
9397 if (pixel_info != (MemoryInfo *) NULL)
9398 pixel_info=RelinquishVirtualMemory(pixel_info);
9400 if (quantum_info != (QuantumInfo *) NULL)
9401 quantum_info=DestroyQuantumInfo(quantum_info);
9403 if (ping_have_blob != MagickFalse)
9404 (void) CloseBlob(image);
9405 image_info=DestroyImageInfo(image_info);
9406 image=DestroyImage(image);
9407 return(MagickFalse);
9410 /* { For navigation to end of SETJMP-protected block. Within this
9411 * block, use png_error() instead of Throwing an Exception, to ensure
9412 * that libpng is able to clean up, and that the semaphore is unlocked.
9415 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9416 LockSemaphoreInfo(ping_semaphore);
9419 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9420 /* Allow benign errors */
9421 png_set_benign_errors(ping, 1);
9424 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9425 /* Reject images with too many rows or columns */
9426 png_set_user_limits(ping,
9427 (png_uint_32) MagickMin(0x7fffffffL,
9428 GetMagickResourceLimit(WidthResource)),
9429 (png_uint_32) MagickMin(0x7fffffffL,
9430 GetMagickResourceLimit(HeightResource)));
9431 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9434 Prepare PNG for writing.
9437 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9438 if (mng_info->write_mng)
9440 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9441 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9442 /* Disable new libpng-1.5.10 feature when writing a MNG because
9443 * zero-length PLTE is OK
9445 png_set_check_for_invalid_index (ping, 0);
9450 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9451 if (mng_info->write_mng)
9452 png_permit_empty_plte(ping,MagickTrue);
9459 ping_width=(png_uint_32) image->columns;
9460 ping_height=(png_uint_32) image->rows;
9462 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9465 if (mng_info->write_png48 || mng_info->write_png64)
9468 if (mng_info->write_png_depth != 0)
9469 image_depth=mng_info->write_png_depth;
9471 /* Adjust requested depth to next higher valid depth if necessary */
9472 if (image_depth > 8)
9475 if ((image_depth > 4) && (image_depth < 8))
9478 if (image_depth == 3)
9481 if (logging != MagickFalse)
9483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9484 " width=%.20g",(double) ping_width);
9485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9486 " height=%.20g",(double) ping_height);
9487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9488 " image_matte=%.20g",(double) image->alpha_trait);
9489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9490 " image->depth=%.20g",(double) image->depth);
9491 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9492 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9495 save_image_depth=image_depth;
9496 ping_bit_depth=(png_byte) save_image_depth;
9499 #if defined(PNG_pHYs_SUPPORTED)
9500 if (ping_exclude_pHYs == MagickFalse)
9502 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9503 (!mng_info->write_mng || !mng_info->equal_physs))
9505 if (logging != MagickFalse)
9506 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9507 " Setting up pHYs chunk");
9509 if (image->units == PixelsPerInchResolution)
9511 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9512 ping_pHYs_x_resolution=
9513 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9514 ping_pHYs_y_resolution=
9515 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9518 else if (image->units == PixelsPerCentimeterResolution)
9520 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9521 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9522 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9527 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9528 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9529 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9532 if (logging != MagickFalse)
9533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9534 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9535 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9536 (int) ping_pHYs_unit_type);
9537 ping_have_pHYs = MagickTrue;
9542 if (ping_exclude_bKGD == MagickFalse)
9544 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9550 if (ping_bit_depth == 8)
9553 if (ping_bit_depth == 4)
9556 if (ping_bit_depth == 2)
9559 if (ping_bit_depth == 1)
9562 ping_background.red=(png_uint_16)
9563 (ScaleQuantumToShort(image->background_color.red) & mask);
9565 ping_background.green=(png_uint_16)
9566 (ScaleQuantumToShort(image->background_color.green) & mask);
9568 ping_background.blue=(png_uint_16)
9569 (ScaleQuantumToShort(image->background_color.blue) & mask);
9571 ping_background.gray=(png_uint_16) ping_background.green;
9574 if (logging != MagickFalse)
9576 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9577 " Setting up bKGD chunk (1)");
9578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9579 " background_color index is %d",
9580 (int) ping_background.index);
9582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9583 " ping_bit_depth=%d",ping_bit_depth);
9586 ping_have_bKGD = MagickTrue;
9590 Select the color type.
9595 if (mng_info->IsPalette && mng_info->write_png8)
9597 /* To do: make this a function cause it's used twice, except
9598 for reducing the sample depth from 8. */
9600 number_colors=image_colors;
9602 ping_have_tRNS=MagickFalse;
9607 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9609 if (logging != MagickFalse)
9610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9611 " Setting up PLTE chunk with %d colors (%d)",
9612 number_colors, image_colors);
9614 for (i=0; i < (ssize_t) number_colors; i++)
9616 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9617 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9618 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9619 if (logging != MagickFalse)
9620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9621 #if MAGICKCORE_QUANTUM_DEPTH == 8
9622 " %3ld (%3d,%3d,%3d)",
9624 " %5ld (%5d,%5d,%5d)",
9626 (long) i,palette[i].red,palette[i].green,palette[i].blue);
9630 ping_have_PLTE=MagickTrue;
9631 image_depth=ping_bit_depth;
9634 if (matte != MagickFalse)
9637 Identify which colormap entry is transparent.
9639 assert(number_colors <= 256);
9640 assert(image->colormap != NULL);
9642 for (i=0; i < (ssize_t) number_transparent; i++)
9643 ping_trans_alpha[i]=0;
9646 ping_num_trans=(unsigned short) (number_transparent +
9647 number_semitransparent);
9649 if (ping_num_trans == 0)
9650 ping_have_tRNS=MagickFalse;
9653 ping_have_tRNS=MagickTrue;
9656 if (ping_exclude_bKGD == MagickFalse)
9659 * Identify which colormap entry is the background color.
9662 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9663 if (IsPNGColorEqual(ping_background,image->colormap[i]))
9666 ping_background.index=(png_byte) i;
9668 if (logging != MagickFalse)
9670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9671 " background_color index is %d",
9672 (int) ping_background.index);
9675 } /* end of write_png8 */
9677 else if (mng_info->write_png_colortype == 1)
9679 image_matte=MagickFalse;
9680 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9683 else if (mng_info->write_png24 || mng_info->write_png48 ||
9684 mng_info->write_png_colortype == 3)
9686 image_matte=MagickFalse;
9687 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9690 else if (mng_info->write_png32 || mng_info->write_png64 ||
9691 mng_info->write_png_colortype == 7)
9693 image_matte=MagickTrue;
9694 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9697 else /* mng_info->write_pngNN not specified */
9699 image_depth=ping_bit_depth;
9701 if (mng_info->write_png_colortype != 0)
9703 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9705 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9706 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9707 image_matte=MagickTrue;
9710 image_matte=MagickFalse;
9712 if (logging != MagickFalse)
9713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9714 " PNG colortype %d was specified:",(int) ping_color_type);
9717 else /* write_png_colortype not specified */
9719 if (logging != MagickFalse)
9720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9721 " Selecting PNG colortype:");
9723 ping_color_type=(png_byte) ((matte != MagickFalse)?
9724 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9726 if (image_info->type == TrueColorType)
9728 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9729 image_matte=MagickFalse;
9732 if (image_info->type == TrueColorAlphaType)
9734 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9735 image_matte=MagickTrue;
9738 if (image_info->type == PaletteType ||
9739 image_info->type == PaletteAlphaType)
9740 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9742 if (mng_info->write_png_colortype == 0 &&
9743 image_info->type == UndefinedType)
9745 if (ping_have_color == MagickFalse)
9747 if (image_matte == MagickFalse)
9749 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9750 image_matte=MagickFalse;
9755 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9756 image_matte=MagickTrue;
9761 if (image_matte == MagickFalse)
9763 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9764 image_matte=MagickFalse;
9769 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9770 image_matte=MagickTrue;
9777 if (logging != MagickFalse)
9778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9779 " Selected PNG colortype=%d",ping_color_type);
9781 if (ping_bit_depth < 8)
9783 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9784 ping_color_type == PNG_COLOR_TYPE_RGB ||
9785 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9789 old_bit_depth=ping_bit_depth;
9791 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9793 if (image->alpha_trait == UndefinedPixelTrait && ping_have_non_bw == MagickFalse)
9797 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9802 if (image->colors == 0)
9805 png_error(ping,"image has 0 colors");
9808 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9809 ping_bit_depth <<= 1;
9812 if (logging != MagickFalse)
9814 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9815 " Number of colors: %.20g",(double) image_colors);
9817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9818 " Tentative PNG bit depth: %d",ping_bit_depth);
9821 if (ping_bit_depth < (int) mng_info->write_png_depth)
9822 ping_bit_depth = mng_info->write_png_depth;
9825 image_depth=ping_bit_depth;
9827 if (logging != MagickFalse)
9829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830 " Tentative PNG color type: %s (%.20g)",
9831 PngColorTypeToString(ping_color_type),
9832 (double) ping_color_type);
9834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9835 " image_info->type: %.20g",(double) image_info->type);
9837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9838 " image_depth: %.20g",(double) image_depth);
9840 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9842 " image->depth: %.20g",(double) image->depth);
9844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845 " ping_bit_depth: %.20g",(double) ping_bit_depth);
9848 if (matte != MagickFalse)
9850 if (mng_info->IsPalette)
9852 if (mng_info->write_png_colortype == 0)
9854 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9856 if (ping_have_color != MagickFalse)
9857 ping_color_type=PNG_COLOR_TYPE_RGBA;
9861 * Determine if there is any transparent color.
9863 if (number_transparent + number_semitransparent == 0)
9866 No transparent pixels are present. Change 4 or 6 to 0 or 2.
9869 image_matte=MagickFalse;
9871 if (mng_info->write_png_colortype == 0)
9872 ping_color_type&=0x03;
9882 if (ping_bit_depth == 8)
9885 if (ping_bit_depth == 4)
9888 if (ping_bit_depth == 2)
9891 if (ping_bit_depth == 1)
9894 ping_trans_color.red=(png_uint_16)
9895 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9897 ping_trans_color.green=(png_uint_16)
9898 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9900 ping_trans_color.blue=(png_uint_16)
9901 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9903 ping_trans_color.gray=(png_uint_16)
9904 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
9905 image->colormap)) & mask);
9907 ping_trans_color.index=(png_byte) 0;
9909 ping_have_tRNS=MagickTrue;
9912 if (ping_have_tRNS != MagickFalse)
9915 * Determine if there is one and only one transparent color
9916 * and if so if it is fully transparent.
9918 if (ping_have_cheap_transparency == MagickFalse)
9919 ping_have_tRNS=MagickFalse;
9922 if (ping_have_tRNS != MagickFalse)
9924 if (mng_info->write_png_colortype == 0)
9925 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
9927 if (image_depth == 8)
9929 ping_trans_color.red&=0xff;
9930 ping_trans_color.green&=0xff;
9931 ping_trans_color.blue&=0xff;
9932 ping_trans_color.gray&=0xff;
9938 if (image_depth == 8)
9940 ping_trans_color.red&=0xff;
9941 ping_trans_color.green&=0xff;
9942 ping_trans_color.blue&=0xff;
9943 ping_trans_color.gray&=0xff;
9950 if (ping_have_tRNS != MagickFalse)
9951 image_matte=MagickFalse;
9953 if ((mng_info->IsPalette) &&
9954 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9955 ping_have_color == MagickFalse &&
9956 (image_matte == MagickFalse || image_depth >= 8))
9960 if (image_matte != MagickFalse)
9961 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9963 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9965 ping_color_type=PNG_COLOR_TYPE_GRAY;
9967 if (save_image_depth == 16 && image_depth == 8)
9969 if (logging != MagickFalse)
9971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9972 " Scaling ping_trans_color (0)");
9974 ping_trans_color.gray*=0x0101;
9978 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9979 image_depth=MAGICKCORE_QUANTUM_DEPTH;
9981 if ((image_colors == 0) ||
9982 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9983 image_colors=(int) (one << image_depth);
9985 if (image_depth > 8)
9991 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9993 if(!mng_info->write_png_depth)
9997 while ((int) (one << ping_bit_depth)
9998 < (ssize_t) image_colors)
9999 ping_bit_depth <<= 1;
10003 else if (ping_color_type ==
10004 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10005 mng_info->IsPalette)
10007 /* Check if grayscale is reducible */
10010 depth_4_ok=MagickTrue,
10011 depth_2_ok=MagickTrue,
10012 depth_1_ok=MagickTrue;
10014 for (i=0; i < (ssize_t) image_colors; i++)
10019 intensity=ScaleQuantumToChar(image->colormap[i].red);
10021 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10022 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10023 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10024 depth_2_ok=depth_1_ok=MagickFalse;
10025 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10026 depth_1_ok=MagickFalse;
10029 if (depth_1_ok && mng_info->write_png_depth <= 1)
10032 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10035 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10040 image_depth=ping_bit_depth;
10045 if (mng_info->IsPalette)
10047 number_colors=image_colors;
10049 if (image_depth <= 8)
10054 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10056 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10058 for (i=0; i < (ssize_t) number_colors; i++)
10060 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10061 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10062 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10065 if (logging != MagickFalse)
10066 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10067 " Setting up PLTE chunk with %d colors",
10070 ping_have_PLTE=MagickTrue;
10073 /* color_type is PNG_COLOR_TYPE_PALETTE */
10074 if (mng_info->write_png_depth == 0)
10082 while ((one << ping_bit_depth) < (size_t) number_colors)
10083 ping_bit_depth <<= 1;
10088 if (matte != MagickFalse)
10091 * Set up trans_colors array.
10093 assert(number_colors <= 256);
10095 ping_num_trans=(unsigned short) (number_transparent +
10096 number_semitransparent);
10098 if (ping_num_trans == 0)
10099 ping_have_tRNS=MagickFalse;
10103 if (logging != MagickFalse)
10105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10106 " Scaling ping_trans_color (1)");
10108 ping_have_tRNS=MagickTrue;
10110 for (i=0; i < ping_num_trans; i++)
10112 ping_trans_alpha[i]= (png_byte)
10113 ScaleQuantumToChar(image->colormap[i].alpha);
10123 if (image_depth < 8)
10126 if ((save_image_depth == 16) && (image_depth == 8))
10128 if (logging != MagickFalse)
10130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10131 " Scaling ping_trans_color from (%d,%d,%d)",
10132 (int) ping_trans_color.red,
10133 (int) ping_trans_color.green,
10134 (int) ping_trans_color.blue);
10137 ping_trans_color.red*=0x0101;
10138 ping_trans_color.green*=0x0101;
10139 ping_trans_color.blue*=0x0101;
10140 ping_trans_color.gray*=0x0101;
10142 if (logging != MagickFalse)
10144 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10146 (int) ping_trans_color.red,
10147 (int) ping_trans_color.green,
10148 (int) ping_trans_color.blue);
10153 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10154 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10157 Adjust background and transparency samples in sub-8-bit grayscale files.
10159 if (ping_bit_depth < 8 && ping_color_type ==
10160 PNG_COLOR_TYPE_GRAY)
10168 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10170 if (ping_exclude_bKGD == MagickFalse)
10173 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10174 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10175 &image->background_color))) +.5)));
10177 if (logging != MagickFalse)
10178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10179 " Setting up bKGD chunk (2)");
10180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181 " background_color index is %d",
10182 (int) ping_background.index);
10184 ping_have_bKGD = MagickTrue;
10187 if (logging != MagickFalse)
10188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10189 " Scaling ping_trans_color.gray from %d",
10190 (int)ping_trans_color.gray);
10192 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10193 ping_trans_color.gray)+.5);
10195 if (logging != MagickFalse)
10196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10197 " to %d", (int)ping_trans_color.gray);
10200 if (ping_exclude_bKGD == MagickFalse)
10202 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10205 Identify which colormap entry is the background color.
10208 number_colors=image_colors;
10210 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10211 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10214 ping_background.index=(png_byte) i;
10216 if (logging != MagickFalse)
10218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10219 " Setting up bKGD chunk with index=%d",(int) i);
10222 if (i < (ssize_t) number_colors)
10224 ping_have_bKGD = MagickTrue;
10226 if (logging != MagickFalse)
10228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10229 " background =(%d,%d,%d)",
10230 (int) ping_background.red,
10231 (int) ping_background.green,
10232 (int) ping_background.blue);
10236 else /* Can't happen */
10238 if (logging != MagickFalse)
10239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10240 " No room in PLTE to add bKGD color");
10241 ping_have_bKGD = MagickFalse;
10246 if (logging != MagickFalse)
10247 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10248 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10251 Initialize compression level and filtering.
10253 if (logging != MagickFalse)
10255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10256 " Setting up deflate compression");
10258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10259 " Compression buffer size: 32768");
10262 png_set_compression_buffer_size(ping,32768L);
10264 if (logging != MagickFalse)
10265 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10266 " Compression mem level: 9");
10268 png_set_compression_mem_level(ping, 9);
10270 /* Untangle the "-quality" setting:
10272 Undefined is 0; the default is used.
10277 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10278 zlib default compression level
10280 1-9: the zlib compression level
10284 0-4: the PNG filter method
10286 5: libpng adaptive filtering if compression level > 5
10287 libpng filter type "none" if compression level <= 5
10288 or if image is grayscale or palette
10290 6: libpng adaptive filtering
10292 7: "LOCO" filtering (intrapixel differing) if writing
10293 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10294 and earlier because of a missing "else".
10296 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10297 filtering. Unused prior to IM-6.7.0-10, was same as 6
10299 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10300 Unused prior to IM-6.7.0-10, was same as 6
10302 Note that using the -quality option, not all combinations of
10303 PNG filter type, zlib compression level, and zlib compression
10304 strategy are possible. This will be addressed soon in a
10305 release that accomodates "-define png:compression-strategy", etc.
10309 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10310 image_info->quality;
10314 if (mng_info->write_png_compression_strategy == 0)
10315 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10318 else if (mng_info->write_png_compression_level == 0)
10323 level=(int) MagickMin((ssize_t) quality/10,9);
10325 mng_info->write_png_compression_level = level+1;
10328 if (mng_info->write_png_compression_strategy == 0)
10330 if ((quality %10) == 8 || (quality %10) == 9)
10331 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10332 mng_info->write_png_compression_strategy=Z_RLE+1;
10334 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10338 if (mng_info->write_png_compression_filter == 0)
10339 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10341 if (logging != MagickFalse)
10343 if (mng_info->write_png_compression_level)
10344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10345 " Compression level: %d",
10346 (int) mng_info->write_png_compression_level-1);
10348 if (mng_info->write_png_compression_strategy)
10349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10350 " Compression strategy: %d",
10351 (int) mng_info->write_png_compression_strategy-1);
10353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10354 " Setting up filtering");
10356 if (mng_info->write_png_compression_filter == 6)
10357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10358 " Base filter method: ADAPTIVE");
10359 else if (mng_info->write_png_compression_filter == 0 ||
10360 mng_info->write_png_compression_filter == 1)
10361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10362 " Base filter method: NONE");
10364 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10365 " Base filter method: %d",
10366 (int) mng_info->write_png_compression_filter-1);
10369 if (mng_info->write_png_compression_level != 0)
10370 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10372 if (mng_info->write_png_compression_filter == 6)
10374 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10375 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10377 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10379 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10381 else if (mng_info->write_png_compression_filter == 7 ||
10382 mng_info->write_png_compression_filter == 10)
10383 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10385 else if (mng_info->write_png_compression_filter == 8)
10387 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10388 if (mng_info->write_mng)
10390 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10391 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10392 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10395 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10398 else if (mng_info->write_png_compression_filter == 9)
10399 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10401 else if (mng_info->write_png_compression_filter != 0)
10402 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10403 mng_info->write_png_compression_filter-1);
10405 if (mng_info->write_png_compression_strategy != 0)
10406 png_set_compression_strategy(ping,
10407 mng_info->write_png_compression_strategy-1);
10409 ping_interlace_method=image_info->interlace != NoInterlace;
10411 if (mng_info->write_mng)
10412 png_set_sig_bytes(ping,8);
10414 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10416 if (mng_info->write_png_colortype != 0)
10418 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10419 if (ping_have_color != MagickFalse)
10421 ping_color_type = PNG_COLOR_TYPE_RGB;
10423 if (ping_bit_depth < 8)
10427 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10428 if (ping_have_color != MagickFalse)
10429 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10432 if (ping_need_colortype_warning != MagickFalse ||
10433 ((mng_info->write_png_depth &&
10434 (int) mng_info->write_png_depth != ping_bit_depth) ||
10435 (mng_info->write_png_colortype &&
10436 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10437 mng_info->write_png_colortype != 7 &&
10438 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10440 if (logging != MagickFalse)
10442 if (ping_need_colortype_warning != MagickFalse)
10444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10445 " Image has transparency but tRNS chunk was excluded");
10448 if (mng_info->write_png_depth)
10450 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10451 " Defined png:bit-depth=%u, Computed depth=%u",
10452 mng_info->write_png_depth,
10456 if (mng_info->write_png_colortype)
10458 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10459 " Defined png:color-type=%u, Computed color type=%u",
10460 mng_info->write_png_colortype-1,
10466 "Cannot write image with defined png:bit-depth or png:color-type.");
10469 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10471 /* Add an opaque matte channel */
10472 image->alpha_trait = BlendPixelTrait;
10473 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10475 if (logging != MagickFalse)
10476 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10477 " Added an opaque matte channel");
10480 if (number_transparent != 0 || number_semitransparent != 0)
10482 if (ping_color_type < 4)
10484 ping_have_tRNS=MagickTrue;
10485 if (logging != MagickFalse)
10486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10487 " Setting ping_have_tRNS=MagickTrue.");
10491 if (logging != MagickFalse)
10492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10493 " Writing PNG header chunks");
10495 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10496 ping_bit_depth,ping_color_type,
10497 ping_interlace_method,ping_compression_method,
10498 ping_filter_method);
10500 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10502 png_set_PLTE(ping,ping_info,palette,number_colors);
10504 if (logging != MagickFalse)
10506 for (i=0; i< (ssize_t) number_colors; i++)
10508 if (i < ping_num_trans)
10509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10510 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10512 (int) palette[i].red,
10513 (int) palette[i].green,
10514 (int) palette[i].blue,
10516 (int) ping_trans_alpha[i]);
10518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10519 " PLTE[%d] = (%d,%d,%d)",
10521 (int) palette[i].red,
10522 (int) palette[i].green,
10523 (int) palette[i].blue);
10528 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10529 if (ping_exclude_sRGB != MagickFalse ||
10530 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10532 if ((ping_exclude_tEXt == MagickFalse ||
10533 ping_exclude_zTXt == MagickFalse) &&
10534 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10536 ResetImageProfileIterator(image);
10537 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10539 profile=GetImageProfile(image,name);
10541 if (profile != (StringInfo *) NULL)
10543 #ifdef PNG_WRITE_iCCP_SUPPORTED
10544 if ((LocaleCompare(name,"ICC") == 0) ||
10545 (LocaleCompare(name,"ICM") == 0))
10548 if (ping_exclude_iCCP == MagickFalse)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " Setting up iCCP chunk");
10553 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10554 #if (PNG_LIBPNG_VER < 10500)
10555 (png_charp) GetStringInfoDatum(profile),
10557 (const png_byte *) GetStringInfoDatum(profile),
10559 (png_uint_32) GetStringInfoLength(profile));
10560 ping_have_iCCP = MagickTrue;
10566 if (ping_exclude_zCCP == MagickFalse)
10568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569 " Setting up zTXT chunk with uuencoded ICC");
10570 Magick_png_write_raw_profile(image_info,ping,ping_info,
10571 (unsigned char *) name,(unsigned char *) name,
10572 GetStringInfoDatum(profile),
10573 (png_uint_32) GetStringInfoLength(profile));
10574 ping_have_iCCP = MagickTrue;
10578 if (logging != MagickFalse)
10579 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10580 " Setting up text chunk with %s profile",name);
10582 name=GetNextImageProfile(image);
10587 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10588 if ((mng_info->have_write_global_srgb == 0) &&
10589 ping_have_iCCP != MagickTrue &&
10590 (ping_have_sRGB != MagickFalse ||
10591 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10593 if (ping_exclude_sRGB == MagickFalse)
10596 Note image rendering intent.
10598 if (logging != MagickFalse)
10599 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10600 " Setting up sRGB chunk");
10602 (void) png_set_sRGB(ping,ping_info,(
10603 Magick_RenderingIntent_to_PNG_RenderingIntent(
10604 image->rendering_intent)));
10606 ping_have_sRGB = MagickTrue;
10610 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10613 if (ping_exclude_gAMA == MagickFalse &&
10614 ping_have_iCCP == MagickFalse &&
10615 ping_have_sRGB == MagickFalse &&
10616 (ping_exclude_sRGB == MagickFalse ||
10617 (image->gamma < .45 || image->gamma > .46)))
10619 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10623 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10625 if (logging != MagickFalse)
10626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627 " Setting up gAMA chunk");
10629 png_set_gAMA(ping,ping_info,image->gamma);
10633 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10635 if ((mng_info->have_write_global_chrm == 0) &&
10636 (image->chromaticity.red_primary.x != 0.0))
10639 Note image chromaticity.
10640 Note: if cHRM+gAMA == sRGB write sRGB instead.
10648 wp=image->chromaticity.white_point;
10649 rp=image->chromaticity.red_primary;
10650 gp=image->chromaticity.green_primary;
10651 bp=image->chromaticity.blue_primary;
10653 if (logging != MagickFalse)
10654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655 " Setting up cHRM chunk");
10657 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10663 if (ping_exclude_bKGD == MagickFalse)
10665 if (ping_have_bKGD != MagickFalse)
10667 png_set_bKGD(ping,ping_info,&ping_background);
10668 if (logging != MagickFalse)
10670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10671 " Setting up bKGD chunk");
10672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10673 " background color = (%d,%d,%d)",
10674 (int) ping_background.red,
10675 (int) ping_background.green,
10676 (int) ping_background.blue);
10677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10678 " index = %d, gray=%d",
10679 (int) ping_background.index,
10680 (int) ping_background.gray);
10685 if (ping_exclude_pHYs == MagickFalse)
10687 if (ping_have_pHYs != MagickFalse)
10689 png_set_pHYs(ping,ping_info,
10690 ping_pHYs_x_resolution,
10691 ping_pHYs_y_resolution,
10692 ping_pHYs_unit_type);
10694 if (logging != MagickFalse)
10696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10697 " Setting up pHYs chunk");
10698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10699 " x_resolution=%lu",
10700 (unsigned long) ping_pHYs_x_resolution);
10701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10702 " y_resolution=%lu",
10703 (unsigned long) ping_pHYs_y_resolution);
10704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10706 (unsigned long) ping_pHYs_unit_type);
10711 #if defined(PNG_oFFs_SUPPORTED)
10712 if (ping_exclude_oFFs == MagickFalse)
10714 if (image->page.x || image->page.y)
10716 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10717 (png_int_32) image->page.y, 0);
10719 if (logging != MagickFalse)
10720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721 " Setting up oFFs chunk with x=%d, y=%d, units=0",
10722 (int) image->page.x, (int) image->page.y);
10727 #if defined(PNG_tIME_SUPPORTED)
10728 if (ping_exclude_tIME == MagickFalse)
10733 if (image->taint == MagickFalse)
10735 timestamp=GetImageOption(image_info,"png:tIME");
10737 if (timestamp == (const char *) NULL)
10738 timestamp=GetImageProperty(image,"png:tIME",exception);
10743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10744 " Reset tIME in tainted image");
10746 timestamp=GetImageProperty(image,"date:modify",exception);
10749 if (timestamp != (const char *) NULL)
10750 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10754 if (mng_info->need_blob != MagickFalse)
10756 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10758 png_error(ping,"WriteBlob Failed");
10760 ping_have_blob=MagickTrue;
10763 png_write_info_before_PLTE(ping, ping_info);
10765 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10767 if (logging != MagickFalse)
10769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10770 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10773 if (ping_color_type == 3)
10774 (void) png_set_tRNS(ping, ping_info,
10781 (void) png_set_tRNS(ping, ping_info,
10784 &ping_trans_color);
10786 if (logging != MagickFalse)
10788 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789 " tRNS color =(%d,%d,%d)",
10790 (int) ping_trans_color.red,
10791 (int) ping_trans_color.green,
10792 (int) ping_trans_color.blue);
10797 /* write any png-chunk-b profiles */
10798 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10800 png_write_info(ping,ping_info);
10802 /* write any PNG-chunk-m profiles */
10803 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10805 if (ping_exclude_vpAg == MagickFalse)
10807 if ((image->page.width != 0 && image->page.width != image->columns) ||
10808 (image->page.height != 0 && image->page.height != image->rows))
10813 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
10814 PNGType(chunk,mng_vpAg);
10815 LogPNGChunk(logging,mng_vpAg,9L);
10816 PNGLong(chunk+4,(png_uint_32) image->page.width);
10817 PNGLong(chunk+8,(png_uint_32) image->page.height);
10818 chunk[12]=0; /* unit = pixels */
10819 (void) WriteBlob(image,13,chunk);
10820 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10824 #if (PNG_LIBPNG_VER == 10206)
10825 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10826 #define PNG_HAVE_IDAT 0x04
10827 ping->mode |= PNG_HAVE_IDAT;
10828 #undef PNG_HAVE_IDAT
10831 png_set_packing(ping);
10835 rowbytes=image->columns;
10836 if (image_depth > 8)
10838 switch (ping_color_type)
10840 case PNG_COLOR_TYPE_RGB:
10844 case PNG_COLOR_TYPE_GRAY_ALPHA:
10848 case PNG_COLOR_TYPE_RGBA:
10856 if (logging != MagickFalse)
10858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10859 " Writing PNG image data");
10861 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10862 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10864 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10865 if (pixel_info == (MemoryInfo *) NULL)
10866 png_error(ping,"Allocation of memory for pixels failed");
10867 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10870 Initialize image scanlines.
10872 quantum_info=AcquireQuantumInfo(image_info,image);
10873 if (quantum_info == (QuantumInfo *) NULL)
10874 png_error(ping,"Memory allocation for quantum_info failed");
10875 quantum_info->format=UndefinedQuantumFormat;
10876 SetQuantumDepth(image,quantum_info,image_depth);
10877 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10878 num_passes=png_set_interlace_handling(ping);
10880 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10881 !mng_info->write_png48 && !mng_info->write_png64 &&
10882 !mng_info->write_png32) &&
10883 (mng_info->IsPalette ||
10884 (image_info->type == BilevelType)) &&
10885 image_matte == MagickFalse &&
10886 ping_have_non_bw == MagickFalse)
10888 /* Palette, Bilevel, or Opaque Monochrome */
10889 register const Quantum
10892 SetQuantumDepth(image,quantum_info,8);
10893 for (pass=0; pass < num_passes; pass++)
10896 Convert PseudoClass image to a PNG monochrome image.
10898 for (y=0; y < (ssize_t) image->rows; y++)
10900 if (logging != MagickFalse && y == 0)
10901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10902 " Writing row of pixels (0)");
10904 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10906 if (p == (const Quantum *) NULL)
10909 if (mng_info->IsPalette)
10911 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10912 quantum_info,GrayQuantum,ping_pixels,exception);
10913 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10914 mng_info->write_png_depth &&
10915 mng_info->write_png_depth != old_bit_depth)
10917 /* Undo pixel scaling */
10918 for (i=0; i < (ssize_t) image->columns; i++)
10919 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10920 >> (8-old_bit_depth));
10926 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10927 quantum_info,RedQuantum,ping_pixels,exception);
10930 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10931 for (i=0; i < (ssize_t) image->columns; i++)
10932 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10935 if (logging != MagickFalse && y == 0)
10936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10937 " Writing row of pixels (1)");
10939 png_write_row(ping,ping_pixels);
10941 status=SetImageProgress(image,SaveImageTag,
10942 (MagickOffsetType) (pass * image->rows + y),
10943 num_passes * image->rows);
10945 if (status == MagickFalse)
10951 else /* Not Palette, Bilevel, or Opaque Monochrome */
10953 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10954 !mng_info->write_png48 && !mng_info->write_png64 &&
10955 !mng_info->write_png32) && (image_matte != MagickFalse ||
10956 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10957 (mng_info->IsPalette) && ping_have_color == MagickFalse)
10959 register const Quantum
10962 for (pass=0; pass < num_passes; pass++)
10965 for (y=0; y < (ssize_t) image->rows; y++)
10967 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10969 if (p == (const Quantum *) NULL)
10972 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10974 if (mng_info->IsPalette)
10975 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10976 quantum_info,GrayQuantum,ping_pixels,exception);
10979 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10980 quantum_info,RedQuantum,ping_pixels,exception);
10982 if (logging != MagickFalse && y == 0)
10983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10984 " Writing GRAY PNG pixels (2)");
10987 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10989 if (logging != MagickFalse && y == 0)
10990 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10991 " Writing GRAY_ALPHA PNG pixels (2)");
10993 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10994 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10997 if (logging != MagickFalse && y == 0)
10998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10999 " Writing row of pixels (2)");
11001 png_write_row(ping,ping_pixels);
11003 status=SetImageProgress(image,SaveImageTag,
11004 (MagickOffsetType) (pass * image->rows + y),
11005 num_passes * image->rows);
11007 if (status == MagickFalse)
11015 register const Quantum
11018 for (pass=0; pass < num_passes; pass++)
11020 if ((image_depth > 8) ||
11021 mng_info->write_png24 ||
11022 mng_info->write_png32 ||
11023 mng_info->write_png48 ||
11024 mng_info->write_png64 ||
11025 (!mng_info->write_png8 && !mng_info->IsPalette))
11027 for (y=0; y < (ssize_t) image->rows; y++)
11029 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11031 if (p == (const Quantum *) NULL)
11034 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11036 if (image->storage_class == DirectClass)
11037 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11038 quantum_info,RedQuantum,ping_pixels,exception);
11041 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11042 quantum_info,GrayQuantum,ping_pixels,exception);
11045 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11047 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11048 quantum_info,GrayAlphaQuantum,ping_pixels,
11051 if (logging != MagickFalse && y == 0)
11052 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11053 " Writing GRAY_ALPHA PNG pixels (3)");
11056 else if (image_matte != MagickFalse)
11057 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11058 quantum_info,RGBAQuantum,ping_pixels,exception);
11061 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11062 quantum_info,RGBQuantum,ping_pixels,exception);
11064 if (logging != MagickFalse && y == 0)
11065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11066 " Writing row of pixels (3)");
11068 png_write_row(ping,ping_pixels);
11070 status=SetImageProgress(image,SaveImageTag,
11071 (MagickOffsetType) (pass * image->rows + y),
11072 num_passes * image->rows);
11074 if (status == MagickFalse)
11080 /* not ((image_depth > 8) ||
11081 mng_info->write_png24 || mng_info->write_png32 ||
11082 mng_info->write_png48 || mng_info->write_png64 ||
11083 (!mng_info->write_png8 && !mng_info->IsPalette))
11086 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11087 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11089 if (logging != MagickFalse)
11090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11091 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11093 SetQuantumDepth(image,quantum_info,8);
11097 for (y=0; y < (ssize_t) image->rows; y++)
11099 if (logging != MagickFalse && y == 0)
11100 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11101 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11103 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11105 if (p == (const Quantum *) NULL)
11108 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11110 SetQuantumDepth(image,quantum_info,image->depth);
11112 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11113 quantum_info,GrayQuantum,ping_pixels,exception);
11116 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11118 if (logging != MagickFalse && y == 0)
11119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11120 " Writing GRAY_ALPHA PNG pixels (4)");
11122 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11123 quantum_info,GrayAlphaQuantum,ping_pixels,
11129 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11130 quantum_info,IndexQuantum,ping_pixels,exception);
11132 if (logging != MagickFalse && y <= 2)
11134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11135 " Writing row of non-gray pixels (4)");
11137 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11138 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11139 (int)ping_pixels[0],(int)ping_pixels[1]);
11142 png_write_row(ping,ping_pixels);
11144 status=SetImageProgress(image,SaveImageTag,
11145 (MagickOffsetType) (pass * image->rows + y),
11146 num_passes * image->rows);
11148 if (status == MagickFalse)
11156 if (quantum_info != (QuantumInfo *) NULL)
11157 quantum_info=DestroyQuantumInfo(quantum_info);
11159 if (logging != MagickFalse)
11161 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11162 " Wrote PNG image data");
11164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11165 " Width: %.20g",(double) ping_width);
11167 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11168 " Height: %.20g",(double) ping_height);
11170 if (mng_info->write_png_depth)
11172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11173 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11176 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11177 " PNG bit-depth written: %d",ping_bit_depth);
11179 if (mng_info->write_png_colortype)
11181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11186 " PNG color-type written: %d",ping_color_type);
11188 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11189 " PNG Interlace method: %d",ping_interlace_method);
11192 Generate text chunks after IDAT.
11194 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11196 ResetImagePropertyIterator(image);
11197 property=GetNextImageProperty(image);
11198 while (property != (const char *) NULL)
11203 value=GetImageProperty(image,property,exception);
11205 /* Don't write any "png:" or "jpeg:" properties; those are just for
11206 * "identify" or for passing through to another JPEG
11208 if ((LocaleNCompare(property,"png:",4) != 0 &&
11209 LocaleNCompare(property,"jpeg:",5) != 0) &&
11212 /* Suppress density and units if we wrote a pHYs chunk */
11213 (ping_exclude_pHYs != MagickFalse ||
11214 LocaleCompare(property,"density") != 0 ||
11215 LocaleCompare(property,"units") != 0) &&
11217 /* Suppress the IM-generated Date:create and Date:modify */
11218 (ping_exclude_date == MagickFalse ||
11219 LocaleNCompare(property, "Date:",5) != 0))
11221 if (value != (const char *) NULL)
11224 #if PNG_LIBPNG_VER >= 10400
11225 text=(png_textp) png_malloc(ping,
11226 (png_alloc_size_t) sizeof(png_text));
11228 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11230 text[0].key=(char *) property;
11231 text[0].text=(char *) value;
11232 text[0].text_length=strlen(value);
11234 if (ping_exclude_tEXt != MagickFalse)
11235 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11237 else if (ping_exclude_zTXt != MagickFalse)
11238 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11242 text[0].compression=image_info->compression == NoCompression ||
11243 (image_info->compression == UndefinedCompression &&
11244 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11245 PNG_TEXT_COMPRESSION_zTXt ;
11248 if (logging != MagickFalse)
11250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11251 " Setting up text chunk");
11253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11254 " keyword: '%s'",text[0].key);
11257 png_set_text(ping,ping_info,text,1);
11258 png_free(ping,text);
11261 property=GetNextImageProperty(image);
11265 /* write any PNG-chunk-e profiles */
11266 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11268 if (logging != MagickFalse)
11269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11270 " Writing PNG end info");
11272 png_write_end(ping,ping_info);
11274 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11276 if (mng_info->page.x || mng_info->page.y ||
11277 (ping_width != mng_info->page.width) ||
11278 (ping_height != mng_info->page.height))
11284 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11286 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11287 PNGType(chunk,mng_FRAM);
11288 LogPNGChunk(logging,mng_FRAM,27L);
11290 chunk[5]=0; /* frame name separator (no name) */
11291 chunk[6]=1; /* flag for changing delay, for next frame only */
11292 chunk[7]=0; /* flag for changing frame timeout */
11293 chunk[8]=1; /* flag for changing frame clipping for next frame */
11294 chunk[9]=0; /* flag for changing frame sync_id */
11295 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11296 chunk[14]=0; /* clipping boundaries delta type */
11297 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11299 (png_uint_32) (mng_info->page.x + ping_width));
11300 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11302 (png_uint_32) (mng_info->page.y + ping_height));
11303 (void) WriteBlob(image,31,chunk);
11304 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11305 mng_info->old_framing_mode=4;
11306 mng_info->framing_mode=1;
11310 mng_info->framing_mode=3;
11312 if (mng_info->write_mng && !mng_info->need_fram &&
11313 ((int) image->dispose == 3))
11314 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11317 Free PNG resources.
11320 png_destroy_write_struct(&ping,&ping_info);
11322 pixel_info=RelinquishVirtualMemory(pixel_info);
11324 if (ping_have_blob != MagickFalse)
11325 (void) CloseBlob(image);
11327 image_info=DestroyImageInfo(image_info);
11328 image=DestroyImage(image);
11330 /* Store bit depth actually written */
11331 s[0]=(char) ping_bit_depth;
11334 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11336 if (logging != MagickFalse)
11337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11338 " exit WriteOnePNGImage()");
11340 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11341 UnlockSemaphoreInfo(ping_semaphore);
11344 /* } for navigation to beginning of SETJMP-protected block. Revert to
11345 * Throwing an Exception when an error occurs.
11348 return(MagickTrue);
11349 /* End write one PNG image */
11354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11358 % W r i t e P N G I m a g e %
11362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11364 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11365 % Multiple-image Network Graphics (MNG) image file.
11367 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11369 % The format of the WritePNGImage method is:
11371 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11372 % Image *image,ExceptionInfo *exception)
11374 % A description of each parameter follows:
11376 % o image_info: the image info.
11378 % o image: The image.
11380 % o exception: return any errors or warnings in this structure.
11382 % Returns MagickTrue on success, MagickFalse on failure.
11384 % Communicating with the PNG encoder:
11386 % While the datastream written is always in PNG format and normally would
11387 % be given the "png" file extension, this method also writes the following
11388 % pseudo-formats which are subsets of png:
11390 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11391 % a depth greater than 8, the depth is reduced. If transparency
11392 % is present, the tRNS chunk must only have values 0 and 255
11393 % (i.e., transparency is binary: fully opaque or fully
11394 % transparent). If other values are present they will be
11395 % 50%-thresholded to binary transparency. If more than 256
11396 % colors are present, they will be quantized to the 4-4-4-1,
11397 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11398 % of any resulting fully-transparent pixels is changed to
11399 % the image's background color.
11401 % If you want better quantization or dithering of the colors
11402 % or alpha than that, you need to do it before calling the
11403 % PNG encoder. The pixels contain 8-bit indices even if
11404 % they could be represented with 1, 2, or 4 bits. Grayscale
11405 % images will be written as indexed PNG files even though the
11406 % PNG grayscale type might be slightly more efficient. Please
11407 % note that writing to the PNG8 format may result in loss
11408 % of color and alpha data.
11410 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11411 % chunk can be present to convey binary transparency by naming
11412 % one of the colors as transparent. The only loss incurred
11413 % is reduction of sample depth to 8. If the image has more
11414 % than one transparent color, has semitransparent pixels, or
11415 % has an opaque pixel with the same RGB components as the
11416 % transparent color, an image is not written.
11418 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11419 % transparency is permitted, i.e., the alpha sample for
11420 % each pixel can have any value from 0 to 255. The alpha
11421 % channel is present even if the image is fully opaque.
11422 % The only loss in data is the reduction of the sample depth
11425 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11426 % chunk can be present to convey binary transparency by naming
11427 % one of the colors as transparent. If the image has more
11428 % than one transparent color, has semitransparent pixels, or
11429 % has an opaque pixel with the same RGB components as the
11430 % transparent color, an image is not written.
11432 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11433 % transparency is permitted, i.e., the alpha sample for
11434 % each pixel can have any value from 0 to 65535. The alpha
11435 % channel is present even if the image is fully opaque.
11437 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11438 % image, if the input was a PNG, is written. If these values
11439 % cannot be found, or if the pixels have been changed in a way
11440 % that makes this impossible, then "PNG00" falls back to the
11441 % regular "PNG" format.
11443 % o -define: For more precise control of the PNG output, you can use the
11444 % Image options "png:bit-depth" and "png:color-type". These
11445 % can be set from the commandline with "-define" and also
11446 % from the application programming interfaces. The options
11447 % are case-independent and are converted to lowercase before
11448 % being passed to this encoder.
11450 % png:color-type can be 0, 2, 3, 4, or 6.
11452 % When png:color-type is 0 (Grayscale), png:bit-depth can
11453 % be 1, 2, 4, 8, or 16.
11455 % When png:color-type is 2 (RGB), png:bit-depth can
11458 % When png:color-type is 3 (Indexed), png:bit-depth can
11459 % be 1, 2, 4, or 8. This refers to the number of bits
11460 % used to store the index. The color samples always have
11461 % bit-depth 8 in indexed PNG files.
11463 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11464 % png:bit-depth can be 8 or 16.
11466 % If the image cannot be written without loss with the
11467 % requested bit-depth and color-type, a PNG file will not
11468 % be written, a warning will be issued, and the encoder will
11469 % return MagickFalse.
11471 % Since image encoders should not be responsible for the "heavy lifting",
11472 % the user should make sure that ImageMagick has already reduced the
11473 % image depth and number of colors and limit transparency to binary
11474 % transparency prior to attempting to write the image with depth, color,
11475 % or transparency limitations.
11477 % Note that another definition, "png:bit-depth-written" exists, but it
11478 % is not intended for external use. It is only used internally by the
11479 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11481 % It is possible to request that the PNG encoder write previously-formatted
11482 % ancillary chunks in the output PNG file, using the "-profile" commandline
11483 % option as shown below or by setting the profile via a programming
11486 % -profile PNG-chunk-x:<file>
11488 % where x is a location flag and <file> is a file containing the chunk
11489 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11490 % This encoder will compute the chunk length and CRC, so those must not
11491 % be included in the file.
11493 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11494 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
11495 % of the same type, then add a short unique string after the "x" to prevent
11496 % subsequent profiles from overwriting the preceding ones, e.g.,
11498 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11500 % As of version 6.6.6 the following optimizations are always done:
11502 % o 32-bit depth is reduced to 16.
11503 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
11504 % high byte and low byte are identical.
11505 % o Palette is sorted to remove unused entries and to put a
11506 % transparent color first, if BUILD_PNG_PALETTE is defined.
11507 % o Opaque matte channel is removed when writing an indexed PNG.
11508 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
11509 % this can be done without loss and a larger bit depth N was not
11510 % requested via the "-define png:bit-depth=N" option.
11511 % o If matte channel is present but only one transparent color is
11512 % present, RGB+tRNS is written instead of RGBA
11513 % o Opaque matte channel is removed (or added, if color-type 4 or 6
11514 % was requested when converting an opaque image).
11516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11518 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11519 Image *image,ExceptionInfo *exception)
11524 have_mng_structure,
11539 assert(image_info != (const ImageInfo *) NULL);
11540 assert(image_info->signature == MagickCoreSignature);
11541 assert(image != (Image *) NULL);
11542 assert(image->signature == MagickCoreSignature);
11543 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11544 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11546 Allocate a MngInfo structure.
11548 have_mng_structure=MagickFalse;
11549 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11551 if (mng_info == (MngInfo *) NULL)
11552 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11555 Initialize members of the MngInfo structure.
11557 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11558 mng_info->image=image;
11559 mng_info->equal_backgrounds=MagickTrue;
11560 have_mng_structure=MagickTrue;
11562 /* See if user has requested a specific PNG subformat */
11564 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11565 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11566 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11567 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11568 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11570 value=GetImageOption(image_info,"png:format");
11572 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11574 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575 " Format=%s",value);
11577 mng_info->write_png8 = MagickFalse;
11578 mng_info->write_png24 = MagickFalse;
11579 mng_info->write_png32 = MagickFalse;
11580 mng_info->write_png48 = MagickFalse;
11581 mng_info->write_png64 = MagickFalse;
11583 if (LocaleCompare(value,"png8") == 0)
11584 mng_info->write_png8 = MagickTrue;
11586 else if (LocaleCompare(value,"png24") == 0)
11587 mng_info->write_png24 = MagickTrue;
11589 else if (LocaleCompare(value,"png32") == 0)
11590 mng_info->write_png32 = MagickTrue;
11592 else if (LocaleCompare(value,"png48") == 0)
11593 mng_info->write_png48 = MagickTrue;
11595 else if (LocaleCompare(value,"png64") == 0)
11596 mng_info->write_png64 = MagickTrue;
11598 else if ((LocaleCompare(value,"png00") == 0) ||
11599 LocaleCompare(image_info->magick,"PNG00") == 0)
11601 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11602 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11604 if (value != (char *) NULL)
11606 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11607 " png00 inherited bit depth=%s",value);
11609 if (LocaleCompare(value,"1") == 0)
11610 mng_info->write_png_depth = 1;
11612 else if (LocaleCompare(value,"2") == 0)
11613 mng_info->write_png_depth = 2;
11615 else if (LocaleCompare(value,"4") == 0)
11616 mng_info->write_png_depth = 4;
11618 else if (LocaleCompare(value,"8") == 0)
11619 mng_info->write_png_depth = 8;
11621 else if (LocaleCompare(value,"16") == 0)
11622 mng_info->write_png_depth = 16;
11625 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11627 if (value != (char *) NULL)
11629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11630 " png00 inherited color type=%s",value);
11632 if (LocaleCompare(value,"0") == 0)
11633 mng_info->write_png_colortype = 1;
11635 else if (LocaleCompare(value,"2") == 0)
11636 mng_info->write_png_colortype = 3;
11638 else if (LocaleCompare(value,"3") == 0)
11639 mng_info->write_png_colortype = 4;
11641 else if (LocaleCompare(value,"4") == 0)
11642 mng_info->write_png_colortype = 5;
11644 else if (LocaleCompare(value,"6") == 0)
11645 mng_info->write_png_colortype = 7;
11650 if (mng_info->write_png8)
11652 mng_info->write_png_colortype = /* 3 */ 4;
11653 mng_info->write_png_depth = 8;
11657 if (mng_info->write_png24)
11659 mng_info->write_png_colortype = /* 2 */ 3;
11660 mng_info->write_png_depth = 8;
11663 if (image->alpha_trait != UndefinedPixelTrait)
11664 (void) SetImageType(image,TrueColorAlphaType,exception);
11667 (void) SetImageType(image,TrueColorType,exception);
11669 (void) SyncImage(image,exception);
11672 if (mng_info->write_png32)
11674 mng_info->write_png_colortype = /* 6 */ 7;
11675 mng_info->write_png_depth = 8;
11677 image->alpha_trait = BlendPixelTrait;
11679 (void) SetImageType(image,TrueColorAlphaType,exception);
11680 (void) SyncImage(image,exception);
11683 if (mng_info->write_png48)
11685 mng_info->write_png_colortype = /* 2 */ 3;
11686 mng_info->write_png_depth = 16;
11689 if (image->alpha_trait != UndefinedPixelTrait)
11690 (void) SetImageType(image,TrueColorAlphaType,exception);
11693 (void) SetImageType(image,TrueColorType,exception);
11695 (void) SyncImage(image,exception);
11698 if (mng_info->write_png64)
11700 mng_info->write_png_colortype = /* 6 */ 7;
11701 mng_info->write_png_depth = 16;
11703 image->alpha_trait = BlendPixelTrait;
11705 (void) SetImageType(image,TrueColorAlphaType,exception);
11706 (void) SyncImage(image,exception);
11709 value=GetImageOption(image_info,"png:bit-depth");
11711 if (value != (char *) NULL)
11713 if (LocaleCompare(value,"1") == 0)
11714 mng_info->write_png_depth = 1;
11716 else if (LocaleCompare(value,"2") == 0)
11717 mng_info->write_png_depth = 2;
11719 else if (LocaleCompare(value,"4") == 0)
11720 mng_info->write_png_depth = 4;
11722 else if (LocaleCompare(value,"8") == 0)
11723 mng_info->write_png_depth = 8;
11725 else if (LocaleCompare(value,"16") == 0)
11726 mng_info->write_png_depth = 16;
11729 (void) ThrowMagickException(exception,
11730 GetMagickModule(),CoderWarning,
11731 "ignoring invalid defined png:bit-depth",
11734 if (logging != MagickFalse)
11735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11736 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11739 value=GetImageOption(image_info,"png:color-type");
11741 if (value != (char *) NULL)
11743 /* We must store colortype+1 because 0 is a valid colortype */
11744 if (LocaleCompare(value,"0") == 0)
11745 mng_info->write_png_colortype = 1;
11747 else if (LocaleCompare(value,"1") == 0)
11748 mng_info->write_png_colortype = 2;
11750 else if (LocaleCompare(value,"2") == 0)
11751 mng_info->write_png_colortype = 3;
11753 else if (LocaleCompare(value,"3") == 0)
11754 mng_info->write_png_colortype = 4;
11756 else if (LocaleCompare(value,"4") == 0)
11757 mng_info->write_png_colortype = 5;
11759 else if (LocaleCompare(value,"6") == 0)
11760 mng_info->write_png_colortype = 7;
11763 (void) ThrowMagickException(exception,
11764 GetMagickModule(),CoderWarning,
11765 "ignoring invalid defined png:color-type",
11768 if (logging != MagickFalse)
11769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11770 " png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11773 /* Check for chunks to be excluded:
11775 * The default is to not exclude any known chunks except for any
11776 * listed in the "unused_chunks" array, above.
11778 * Chunks can be listed for exclusion via a "png:exclude-chunk"
11779 * define (in the image properties or in the image artifacts)
11780 * or via a mng_info member. For convenience, in addition
11781 * to or instead of a comma-separated list of chunks, the
11782 * "exclude-chunk" string can be simply "all" or "none".
11784 * The exclude-chunk define takes priority over the mng_info.
11786 * A "png:include-chunk" define takes priority over both the
11787 * mng_info and the "png:exclude-chunk" define. Like the
11788 * "exclude-chunk" string, it can define "all" or "none" as
11789 * well as a comma-separated list. Chunks that are unknown to
11790 * ImageMagick are always excluded, regardless of their "copy-safe"
11791 * status according to the PNG specification, and even if they
11792 * appear in the "include-chunk" list. Such defines appearing among
11793 * the image options take priority over those found among the image
11796 * Finally, all chunks listed in the "unused_chunks" array are
11797 * automatically excluded, regardless of the other instructions
11800 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11801 * will not be written and the gAMA chunk will only be written if it
11802 * is not between .45 and .46, or approximately (1.0/2.2).
11804 * If you exclude tRNS and the image has transparency, the colortype
11805 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11807 * The -strip option causes StripImage() to set the png:include-chunk
11808 * artifact to "none,trns,gama".
11811 mng_info->ping_exclude_bKGD=MagickFalse;
11812 mng_info->ping_exclude_cHRM=MagickFalse;
11813 mng_info->ping_exclude_date=MagickFalse;
11814 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11815 mng_info->ping_exclude_gAMA=MagickFalse;
11816 mng_info->ping_exclude_iCCP=MagickFalse;
11817 /* mng_info->ping_exclude_iTXt=MagickFalse; */
11818 mng_info->ping_exclude_oFFs=MagickFalse;
11819 mng_info->ping_exclude_pHYs=MagickFalse;
11820 mng_info->ping_exclude_sRGB=MagickFalse;
11821 mng_info->ping_exclude_tEXt=MagickFalse;
11822 mng_info->ping_exclude_tIME=MagickFalse;
11823 mng_info->ping_exclude_tRNS=MagickFalse;
11824 mng_info->ping_exclude_vpAg=MagickFalse;
11825 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11826 mng_info->ping_exclude_zTXt=MagickFalse;
11828 mng_info->ping_preserve_colormap=MagickFalse;
11830 value=GetImageOption(image_info,"png:preserve-colormap");
11832 value=GetImageArtifact(image,"png:preserve-colormap");
11834 mng_info->ping_preserve_colormap=MagickTrue;
11836 mng_info->ping_preserve_iCCP=MagickFalse;
11838 value=GetImageOption(image_info,"png:preserve-iCCP");
11840 value=GetImageArtifact(image,"png:preserve-iCCP");
11842 mng_info->ping_preserve_iCCP=MagickTrue;
11844 /* These compression-level, compression-strategy, and compression-filter
11845 * defines take precedence over values from the -quality option.
11847 value=GetImageOption(image_info,"png:compression-level");
11849 value=GetImageArtifact(image,"png:compression-level");
11852 /* We have to add 1 to everything because 0 is a valid input,
11853 * and we want to use 0 (the default) to mean undefined.
11855 if (LocaleCompare(value,"0") == 0)
11856 mng_info->write_png_compression_level = 1;
11858 else if (LocaleCompare(value,"1") == 0)
11859 mng_info->write_png_compression_level = 2;
11861 else if (LocaleCompare(value,"2") == 0)
11862 mng_info->write_png_compression_level = 3;
11864 else if (LocaleCompare(value,"3") == 0)
11865 mng_info->write_png_compression_level = 4;
11867 else if (LocaleCompare(value,"4") == 0)
11868 mng_info->write_png_compression_level = 5;
11870 else if (LocaleCompare(value,"5") == 0)
11871 mng_info->write_png_compression_level = 6;
11873 else if (LocaleCompare(value,"6") == 0)
11874 mng_info->write_png_compression_level = 7;
11876 else if (LocaleCompare(value,"7") == 0)
11877 mng_info->write_png_compression_level = 8;
11879 else if (LocaleCompare(value,"8") == 0)
11880 mng_info->write_png_compression_level = 9;
11882 else if (LocaleCompare(value,"9") == 0)
11883 mng_info->write_png_compression_level = 10;
11886 (void) ThrowMagickException(exception,
11887 GetMagickModule(),CoderWarning,
11888 "ignoring invalid defined png:compression-level",
11892 value=GetImageOption(image_info,"png:compression-strategy");
11894 value=GetImageArtifact(image,"png:compression-strategy");
11897 if (LocaleCompare(value,"0") == 0)
11898 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11900 else if (LocaleCompare(value,"1") == 0)
11901 mng_info->write_png_compression_strategy = Z_FILTERED+1;
11903 else if (LocaleCompare(value,"2") == 0)
11904 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11906 else if (LocaleCompare(value,"3") == 0)
11907 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
11908 mng_info->write_png_compression_strategy = Z_RLE+1;
11910 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11913 else if (LocaleCompare(value,"4") == 0)
11914 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
11915 mng_info->write_png_compression_strategy = Z_FIXED+1;
11917 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11921 (void) ThrowMagickException(exception,
11922 GetMagickModule(),CoderWarning,
11923 "ignoring invalid defined png:compression-strategy",
11927 value=GetImageOption(image_info,"png:compression-filter");
11929 value=GetImageArtifact(image,"png:compression-filter");
11932 /* To do: combinations of filters allowed by libpng
11933 * masks 0x08 through 0xf8
11935 * Implement this as a comma-separated list of 0,1,2,3,4,5
11936 * where 5 is a special case meaning PNG_ALL_FILTERS.
11939 if (LocaleCompare(value,"0") == 0)
11940 mng_info->write_png_compression_filter = 1;
11942 else if (LocaleCompare(value,"1") == 0)
11943 mng_info->write_png_compression_filter = 2;
11945 else if (LocaleCompare(value,"2") == 0)
11946 mng_info->write_png_compression_filter = 3;
11948 else if (LocaleCompare(value,"3") == 0)
11949 mng_info->write_png_compression_filter = 4;
11951 else if (LocaleCompare(value,"4") == 0)
11952 mng_info->write_png_compression_filter = 5;
11954 else if (LocaleCompare(value,"5") == 0)
11955 mng_info->write_png_compression_filter = 6;
11958 (void) ThrowMagickException(exception,
11959 GetMagickModule(),CoderWarning,
11960 "ignoring invalid defined png:compression-filter",
11964 for (source=0; source<8; source++)
11969 value=GetImageOption(image_info,"png:exclude-chunks");
11972 value=GetImageArtifact(image,"png:exclude-chunks");
11975 value=GetImageOption(image_info,"png:exclude-chunk");
11978 value=GetImageArtifact(image,"png:exclude-chunk");
11981 value=GetImageOption(image_info,"png:include-chunks");
11984 value=GetImageArtifact(image,"png:include-chunks");
11987 value=GetImageOption(image_info,"png:include-chunk");
11990 value=GetImageArtifact(image,"png:include-chunk");
11996 excluding = MagickTrue;
11998 excluding = MagickFalse;
12000 if (logging != MagickFalse)
12002 if (source == 0 || source == 2)
12003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12004 " png:exclude-chunk=%s found in image options.\n", value);
12005 else if (source == 1 || source == 3)
12006 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12007 " png:exclude-chunk=%s found in image artifacts.\n", value);
12008 else if (source == 4 || source == 6)
12009 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12010 " png:include-chunk=%s found in image options.\n", value);
12011 else /* if (source == 5 || source == 7) */
12012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12013 " png:include-chunk=%s found in image artifacts.\n", value);
12016 if (IsOptionMember("all",value) != MagickFalse)
12018 mng_info->ping_exclude_bKGD=excluding;
12019 mng_info->ping_exclude_cHRM=excluding;
12020 mng_info->ping_exclude_date=excluding;
12021 mng_info->ping_exclude_EXIF=excluding;
12022 mng_info->ping_exclude_gAMA=excluding;
12023 mng_info->ping_exclude_iCCP=excluding;
12024 /* mng_info->ping_exclude_iTXt=excluding; */
12025 mng_info->ping_exclude_oFFs=excluding;
12026 mng_info->ping_exclude_pHYs=excluding;
12027 mng_info->ping_exclude_sRGB=excluding;
12028 mng_info->ping_exclude_tEXt=excluding;
12029 mng_info->ping_exclude_tIME=excluding;
12030 mng_info->ping_exclude_tRNS=excluding;
12031 mng_info->ping_exclude_vpAg=excluding;
12032 mng_info->ping_exclude_zCCP=excluding;
12033 mng_info->ping_exclude_zTXt=excluding;
12036 if (IsOptionMember("none",value) != MagickFalse)
12038 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12040 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12042 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12044 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12046 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12048 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12050 /* mng_info->ping_exclude_iTXt=!excluding; */
12051 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12053 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12055 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12057 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12059 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12061 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12063 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12065 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12067 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12071 if (IsOptionMember("bkgd",value) != MagickFalse)
12072 mng_info->ping_exclude_bKGD=excluding;
12074 if (IsOptionMember("chrm",value) != MagickFalse)
12075 mng_info->ping_exclude_cHRM=excluding;
12077 if (IsOptionMember("date",value) != MagickFalse)
12078 mng_info->ping_exclude_date=excluding;
12080 if (IsOptionMember("exif",value) != MagickFalse)
12081 mng_info->ping_exclude_EXIF=excluding;
12083 if (IsOptionMember("gama",value) != MagickFalse)
12084 mng_info->ping_exclude_gAMA=excluding;
12086 if (IsOptionMember("iccp",value) != MagickFalse)
12087 mng_info->ping_exclude_iCCP=excluding;
12090 if (IsOptionMember("itxt",value) != MagickFalse)
12091 mng_info->ping_exclude_iTXt=excluding;
12094 if (IsOptionMember("offs",value) != MagickFalse)
12095 mng_info->ping_exclude_oFFs=excluding;
12097 if (IsOptionMember("phys",value) != MagickFalse)
12098 mng_info->ping_exclude_pHYs=excluding;
12100 if (IsOptionMember("srgb",value) != MagickFalse)
12101 mng_info->ping_exclude_sRGB=excluding;
12103 if (IsOptionMember("text",value) != MagickFalse)
12104 mng_info->ping_exclude_tEXt=excluding;
12106 if (IsOptionMember("time",value) != MagickFalse)
12107 mng_info->ping_exclude_tIME=excluding;
12109 if (IsOptionMember("trns",value) != MagickFalse)
12110 mng_info->ping_exclude_tRNS=excluding;
12112 if (IsOptionMember("vpag",value) != MagickFalse)
12113 mng_info->ping_exclude_vpAg=excluding;
12115 if (IsOptionMember("zccp",value) != MagickFalse)
12116 mng_info->ping_exclude_zCCP=excluding;
12118 if (IsOptionMember("ztxt",value) != MagickFalse)
12119 mng_info->ping_exclude_zTXt=excluding;
12122 if (logging != MagickFalse)
12124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12125 " Chunks to be excluded from the output png:");
12126 if (mng_info->ping_exclude_bKGD != MagickFalse)
12127 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12129 if (mng_info->ping_exclude_cHRM != MagickFalse)
12130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12132 if (mng_info->ping_exclude_date != MagickFalse)
12133 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12135 if (mng_info->ping_exclude_EXIF != MagickFalse)
12136 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12138 if (mng_info->ping_exclude_gAMA != MagickFalse)
12139 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12141 if (mng_info->ping_exclude_iCCP != MagickFalse)
12142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12145 if (mng_info->ping_exclude_iTXt != MagickFalse)
12146 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12150 if (mng_info->ping_exclude_oFFs != MagickFalse)
12151 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12153 if (mng_info->ping_exclude_pHYs != MagickFalse)
12154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12156 if (mng_info->ping_exclude_sRGB != MagickFalse)
12157 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12159 if (mng_info->ping_exclude_tEXt != MagickFalse)
12160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12162 if (mng_info->ping_exclude_tIME != MagickFalse)
12163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12165 if (mng_info->ping_exclude_tRNS != MagickFalse)
12166 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12168 if (mng_info->ping_exclude_vpAg != MagickFalse)
12169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12171 if (mng_info->ping_exclude_zCCP != MagickFalse)
12172 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12174 if (mng_info->ping_exclude_zTXt != MagickFalse)
12175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12179 mng_info->need_blob = MagickTrue;
12181 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12183 MngInfoFreeStruct(mng_info,&have_mng_structure);
12185 if (logging != MagickFalse)
12186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12191 #if defined(JNG_SUPPORTED)
12193 /* Write one JNG image */
12194 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12195 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12216 jng_alpha_compression_method,
12217 jng_alpha_sample_depth,
12225 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12226 " Enter WriteOneJNGImage()");
12228 blob=(unsigned char *) NULL;
12229 jpeg_image=(Image *) NULL;
12230 jpeg_image_info=(ImageInfo *) NULL;
12234 transparent=image_info->type==GrayscaleAlphaType ||
12235 image_info->type==TrueColorAlphaType ||
12236 image->alpha_trait != UndefinedPixelTrait;
12238 jng_alpha_sample_depth = 0;
12240 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12242 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12244 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12245 image_info->quality;
12247 if (jng_alpha_quality >= 1000)
12248 jng_alpha_quality /= 1000;
12252 if (transparent != 0)
12256 /* Create JPEG blob, image, and image_info */
12257 if (logging != MagickFalse)
12258 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12259 " Creating jpeg_image_info for alpha.");
12261 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12263 if (jpeg_image_info == (ImageInfo *) NULL)
12264 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12266 if (logging != MagickFalse)
12267 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12268 " Creating jpeg_image.");
12270 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12271 if (jpeg_image == (Image *) NULL)
12272 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12273 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12274 jpeg_image->alpha_trait=UndefinedPixelTrait;
12275 jpeg_image->quality=jng_alpha_quality;
12276 jpeg_image_info->type=GrayscaleType;
12277 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12278 (void) AcquireUniqueFilename(jpeg_image->filename);
12279 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12280 "%s",jpeg_image->filename);
12284 jng_alpha_compression_method=0;
12286 jng_alpha_sample_depth=0;
12289 /* To do: check bit depth of PNG alpha channel */
12291 /* Check if image is grayscale. */
12292 if (image_info->type != TrueColorAlphaType && image_info->type !=
12293 TrueColorType && SetImageGray(image,exception))
12296 if (logging != MagickFalse)
12298 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12299 " JNG Quality = %d",(int) jng_quality);
12300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12301 " JNG Color Type = %d",jng_color_type);
12302 if (transparent != 0)
12304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12305 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12307 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12309 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12313 if (transparent != 0)
12315 if (jng_alpha_compression_method==0)
12320 /* Encode alpha as a grayscale PNG blob */
12321 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12323 if (status == MagickFalse)
12324 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12326 if (logging != MagickFalse)
12327 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12328 " Creating PNG blob.");
12330 (void) CopyMagickString(jpeg_image_info->magick,"PNG",MagickPathExtent);
12331 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12332 jpeg_image_info->interlace=NoInterlace;
12334 /* Exclude all ancillary chunks */
12335 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12337 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12338 &length,exception);
12340 /* Retrieve sample depth used */
12341 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12342 if (value != (char *) NULL)
12343 jng_alpha_sample_depth= (unsigned int) value[0];
12347 /* Encode alpha as a grayscale JPEG blob */
12349 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12351 if (status == MagickFalse)
12352 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12355 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12356 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12357 jpeg_image_info->interlace=NoInterlace;
12358 if (logging != MagickFalse)
12359 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12360 " Creating blob.");
12361 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12363 jng_alpha_sample_depth=8;
12365 if (logging != MagickFalse)
12366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12367 " Successfully read jpeg_image into a blob, length=%.20g.",
12371 /* Destroy JPEG image and image_info */
12372 jpeg_image=DestroyImage(jpeg_image);
12373 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12374 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12377 /* Write JHDR chunk */
12378 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12379 PNGType(chunk,mng_JHDR);
12380 LogPNGChunk(logging,mng_JHDR,16L);
12381 PNGLong(chunk+4,(png_uint_32) image->columns);
12382 PNGLong(chunk+8,(png_uint_32) image->rows);
12383 chunk[12]=jng_color_type;
12384 chunk[13]=8; /* sample depth */
12385 chunk[14]=8; /*jng_image_compression_method */
12386 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12387 chunk[16]=jng_alpha_sample_depth;
12388 chunk[17]=jng_alpha_compression_method;
12389 chunk[18]=0; /*jng_alpha_filter_method */
12390 chunk[19]=0; /*jng_alpha_interlace_method */
12391 (void) WriteBlob(image,20,chunk);
12392 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12393 if (logging != MagickFalse)
12395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12396 " JNG width:%15lu",(unsigned long) image->columns);
12398 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12399 " JNG height:%14lu",(unsigned long) image->rows);
12401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12402 " JNG color type:%10d",jng_color_type);
12404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12405 " JNG sample depth:%8d",8);
12407 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12408 " JNG compression:%9d",8);
12410 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12411 " JNG interlace:%11d",0);
12413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12414 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12416 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12417 " JNG alpha compression:%3d",jng_alpha_compression_method);
12419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12420 " JNG alpha filter:%8d",0);
12422 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12423 " JNG alpha interlace:%5d",0);
12426 /* Write any JNG-chunk-b profiles */
12427 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12430 Write leading ancillary chunks
12433 if (transparent != 0)
12436 Write JNG bKGD chunk
12447 if (jng_color_type == 8 || jng_color_type == 12)
12451 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12452 PNGType(chunk,mng_bKGD);
12453 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12454 red=ScaleQuantumToChar(image->background_color.red);
12455 green=ScaleQuantumToChar(image->background_color.green);
12456 blue=ScaleQuantumToChar(image->background_color.blue);
12463 (void) WriteBlob(image,(size_t) num_bytes,chunk);
12464 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12467 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12470 Write JNG sRGB chunk
12472 (void) WriteBlobMSBULong(image,1L);
12473 PNGType(chunk,mng_sRGB);
12474 LogPNGChunk(logging,mng_sRGB,1L);
12476 if (image->rendering_intent != UndefinedIntent)
12477 chunk[4]=(unsigned char)
12478 Magick_RenderingIntent_to_PNG_RenderingIntent(
12479 (image->rendering_intent));
12482 chunk[4]=(unsigned char)
12483 Magick_RenderingIntent_to_PNG_RenderingIntent(
12484 (PerceptualIntent));
12486 (void) WriteBlob(image,5,chunk);
12487 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12491 if (image->gamma != 0.0)
12494 Write JNG gAMA chunk
12496 (void) WriteBlobMSBULong(image,4L);
12497 PNGType(chunk,mng_gAMA);
12498 LogPNGChunk(logging,mng_gAMA,4L);
12499 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12500 (void) WriteBlob(image,8,chunk);
12501 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12504 if ((mng_info->equal_chrms == MagickFalse) &&
12505 (image->chromaticity.red_primary.x != 0.0))
12511 Write JNG cHRM chunk
12513 (void) WriteBlobMSBULong(image,32L);
12514 PNGType(chunk,mng_cHRM);
12515 LogPNGChunk(logging,mng_cHRM,32L);
12516 primary=image->chromaticity.white_point;
12517 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12518 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12519 primary=image->chromaticity.red_primary;
12520 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12521 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12522 primary=image->chromaticity.green_primary;
12523 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12524 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12525 primary=image->chromaticity.blue_primary;
12526 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12527 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12528 (void) WriteBlob(image,36,chunk);
12529 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12533 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12536 Write JNG pHYs chunk
12538 (void) WriteBlobMSBULong(image,9L);
12539 PNGType(chunk,mng_pHYs);
12540 LogPNGChunk(logging,mng_pHYs,9L);
12541 if (image->units == PixelsPerInchResolution)
12543 PNGLong(chunk+4,(png_uint_32)
12544 (image->resolution.x*100.0/2.54+0.5));
12546 PNGLong(chunk+8,(png_uint_32)
12547 (image->resolution.y*100.0/2.54+0.5));
12554 if (image->units == PixelsPerCentimeterResolution)
12556 PNGLong(chunk+4,(png_uint_32)
12557 (image->resolution.x*100.0+0.5));
12559 PNGLong(chunk+8,(png_uint_32)
12560 (image->resolution.y*100.0+0.5));
12567 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12568 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12572 (void) WriteBlob(image,13,chunk);
12573 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12576 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12579 Write JNG oFFs chunk
12581 (void) WriteBlobMSBULong(image,9L);
12582 PNGType(chunk,mng_oFFs);
12583 LogPNGChunk(logging,mng_oFFs,9L);
12584 PNGsLong(chunk+4,(ssize_t) (image->page.x));
12585 PNGsLong(chunk+8,(ssize_t) (image->page.y));
12587 (void) WriteBlob(image,13,chunk);
12588 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12590 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12592 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
12593 PNGType(chunk,mng_vpAg);
12594 LogPNGChunk(logging,mng_vpAg,9L);
12595 PNGLong(chunk+4,(png_uint_32) image->page.width);
12596 PNGLong(chunk+8,(png_uint_32) image->page.height);
12597 chunk[12]=0; /* unit = pixels */
12598 (void) WriteBlob(image,13,chunk);
12599 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12602 if (transparent != 0)
12604 if (jng_alpha_compression_method==0)
12612 /* Write IDAT chunk header */
12613 if (logging != MagickFalse)
12614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12615 " Write IDAT chunks from blob, length=%.20g.",(double)
12618 /* Copy IDAT chunks */
12621 for (i=8; i<(ssize_t) length; i+=len+12)
12623 len=(size_t) (*p) << 24;
12624 len|=(size_t) (*(p+1)) << 16;
12625 len|=(size_t) (*(p+2)) << 8;
12626 len|=(size_t) (*(p+3));
12629 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12631 /* Found an IDAT chunk. */
12632 (void) WriteBlobMSBULong(image,len);
12633 LogPNGChunk(logging,mng_IDAT,len);
12634 (void) WriteBlob(image,len+4,p);
12635 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12640 if (logging != MagickFalse)
12641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12642 " Skipping %c%c%c%c chunk, length=%.20g.",
12643 *(p),*(p+1),*(p+2),*(p+3),(double) len);
12648 else if (length != 0)
12650 /* Write JDAA chunk header */
12651 if (logging != MagickFalse)
12652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12653 " Write JDAA chunk, length=%.20g.",(double) length);
12654 (void) WriteBlobMSBULong(image,(size_t) length);
12655 PNGType(chunk,mng_JDAA);
12656 LogPNGChunk(logging,mng_JDAA,length);
12657 /* Write JDAT chunk(s) data */
12658 (void) WriteBlob(image,4,chunk);
12659 (void) WriteBlob(image,length,blob);
12660 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12663 blob=(unsigned char *) RelinquishMagickMemory(blob);
12666 /* Encode image as a JPEG blob */
12667 if (logging != MagickFalse)
12668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12669 " Creating jpeg_image_info.");
12670 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12671 if (jpeg_image_info == (ImageInfo *) NULL)
12672 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12674 if (logging != MagickFalse)
12675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12676 " Creating jpeg_image.");
12678 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12679 if (jpeg_image == (Image *) NULL)
12680 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12681 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12683 (void) AcquireUniqueFilename(jpeg_image->filename);
12684 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
12685 jpeg_image->filename);
12687 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12690 if (logging != MagickFalse)
12691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12692 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12693 (double) jpeg_image->rows);
12695 if (status == MagickFalse)
12696 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12698 if (jng_color_type == 8 || jng_color_type == 12)
12699 jpeg_image_info->type=GrayscaleType;
12701 jpeg_image_info->quality=jng_quality;
12702 jpeg_image->quality=jng_quality;
12703 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12704 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12706 if (logging != MagickFalse)
12707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12708 " Creating blob.");
12710 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12713 if (logging != MagickFalse)
12715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716 " Successfully read jpeg_image into a blob, length=%.20g.",
12719 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12720 " Write JDAT chunk, length=%.20g.",(double) length);
12723 /* Write JDAT chunk(s) */
12724 (void) WriteBlobMSBULong(image,(size_t) length);
12725 PNGType(chunk,mng_JDAT);
12726 LogPNGChunk(logging,mng_JDAT,length);
12727 (void) WriteBlob(image,4,chunk);
12728 (void) WriteBlob(image,length,blob);
12729 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12731 jpeg_image=DestroyImage(jpeg_image);
12732 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12733 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12734 blob=(unsigned char *) RelinquishMagickMemory(blob);
12736 /* Write any JNG-chunk-e profiles */
12737 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12739 /* Write IEND chunk */
12740 (void) WriteBlobMSBULong(image,0L);
12741 PNGType(chunk,mng_IEND);
12742 LogPNGChunk(logging,mng_IEND,0);
12743 (void) WriteBlob(image,4,chunk);
12744 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12746 if (logging != MagickFalse)
12747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12748 " exit WriteOneJNGImage()");
12755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12759 % W r i t e J N G I m a g e %
12763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12765 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12767 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
12769 % The format of the WriteJNGImage method is:
12771 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12772 % Image *image,ExceptionInfo *exception)
12774 % A description of each parameter follows:
12776 % o image_info: the image info.
12778 % o image: The image.
12780 % o exception: return any errors or warnings in this structure.
12782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12784 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12785 ExceptionInfo *exception)
12788 have_mng_structure,
12798 assert(image_info != (const ImageInfo *) NULL);
12799 assert(image_info->signature == MagickCoreSignature);
12800 assert(image != (Image *) NULL);
12801 assert(image->signature == MagickCoreSignature);
12802 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12803 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12804 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12805 if (status == MagickFalse)
12807 if ((image->columns > 65535UL) || (image->rows > 65535UL))
12808 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
12811 Allocate a MngInfo structure.
12813 have_mng_structure=MagickFalse;
12814 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12815 if (mng_info == (MngInfo *) NULL)
12816 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12818 Initialize members of the MngInfo structure.
12820 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12821 mng_info->image=image;
12822 have_mng_structure=MagickTrue;
12824 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12826 status=WriteOneJNGImage(mng_info,image_info,image,exception);
12827 (void) CloseBlob(image);
12829 (void) CatchImageException(image);
12830 MngInfoFreeStruct(mng_info,&have_mng_structure);
12831 if (logging != MagickFalse)
12832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12837 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12838 ExceptionInfo *exception)
12847 have_mng_structure,
12850 volatile MagickBooleanType
12862 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12863 defined(PNG_MNG_FEATURES_SUPPORTED)
12866 all_images_are_gray,
12876 volatile unsigned int
12887 #if (PNG_LIBPNG_VER < 10200)
12888 if (image_info->verbose)
12889 printf("Your PNG library (libpng-%s) is rather old.\n",
12890 PNG_LIBPNG_VER_STRING);
12896 assert(image_info != (const ImageInfo *) NULL);
12897 assert(image_info->signature == MagickCoreSignature);
12898 assert(image != (Image *) NULL);
12899 assert(image->signature == MagickCoreSignature);
12900 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12901 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12902 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12903 if (status == MagickFalse)
12907 Allocate a MngInfo structure.
12909 have_mng_structure=MagickFalse;
12910 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12911 if (mng_info == (MngInfo *) NULL)
12912 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12914 Initialize members of the MngInfo structure.
12916 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12917 mng_info->image=image;
12918 have_mng_structure=MagickTrue;
12919 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12922 * See if user has requested a specific PNG subformat to be used
12923 * for all of the PNGs in the MNG being written, e.g.,
12925 * convert *.png png8:animation.mng
12927 * To do: check -define png:bit_depth and png:color_type as well,
12928 * or perhaps use mng:bit_depth and mng:color_type instead for
12932 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12933 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12934 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12936 write_jng=MagickFalse;
12937 if (image_info->compression == JPEGCompression)
12938 write_jng=MagickTrue;
12940 mng_info->adjoin=image_info->adjoin &&
12941 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12943 if (logging != MagickFalse)
12945 /* Log some info about the input */
12949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12950 " Checking input image(s)\n"
12951 " Image_info depth: %.20g, Type: %d",
12952 (double) image_info->depth, image_info->type);
12955 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12959 " Scene: %.20g\n, Image depth: %.20g",
12960 (double) scene++, (double) p->depth);
12962 if (p->alpha_trait != UndefinedPixelTrait)
12963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12970 if (p->storage_class == PseudoClass)
12971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12972 " Storage class: PseudoClass");
12975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12976 " Storage class: DirectClass");
12979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12980 " Number of colors: %.20g",(double) p->colors);
12983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984 " Number of colors: unspecified");
12986 if (mng_info->adjoin == MagickFalse)
12991 use_global_plte=MagickFalse;
12992 all_images_are_gray=MagickFalse;
12993 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12994 need_local_plte=MagickTrue;
12996 need_defi=MagickFalse;
12997 need_matte=MagickFalse;
12998 mng_info->framing_mode=1;
12999 mng_info->old_framing_mode=1;
13002 if (image_info->page != (char *) NULL)
13005 Determine image bounding box.
13007 SetGeometry(image,&mng_info->page);
13008 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13009 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13021 mng_info->page=image->page;
13022 need_geom=MagickTrue;
13023 if (mng_info->page.width || mng_info->page.height)
13024 need_geom=MagickFalse;
13026 Check all the scenes.
13028 initial_delay=image->delay;
13029 need_iterations=MagickFalse;
13030 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13031 mng_info->equal_physs=MagickTrue,
13032 mng_info->equal_gammas=MagickTrue;
13033 mng_info->equal_srgbs=MagickTrue;
13034 mng_info->equal_backgrounds=MagickTrue;
13036 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13037 defined(PNG_MNG_FEATURES_SUPPORTED)
13038 all_images_are_gray=MagickTrue;
13039 mng_info->equal_palettes=MagickFalse;
13040 need_local_plte=MagickFalse;
13042 for (next_image=image; next_image != (Image *) NULL; )
13046 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13047 mng_info->page.width=next_image->columns+next_image->page.x;
13049 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13050 mng_info->page.height=next_image->rows+next_image->page.y;
13053 if (next_image->page.x || next_image->page.y)
13054 need_defi=MagickTrue;
13056 if (next_image->alpha_trait != UndefinedPixelTrait)
13057 need_matte=MagickTrue;
13059 if ((int) next_image->dispose >= BackgroundDispose)
13060 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13061 next_image->page.x || next_image->page.y ||
13062 ((next_image->columns < mng_info->page.width) &&
13063 (next_image->rows < mng_info->page.height)))
13064 mng_info->need_fram=MagickTrue;
13066 if (next_image->iterations)
13067 need_iterations=MagickTrue;
13069 final_delay=next_image->delay;
13071 if (final_delay != initial_delay || final_delay > 1UL*
13072 next_image->ticks_per_second)
13073 mng_info->need_fram=1;
13075 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13076 defined(PNG_MNG_FEATURES_SUPPORTED)
13078 check for global palette possibility.
13080 if (image->alpha_trait != UndefinedPixelTrait)
13081 need_local_plte=MagickTrue;
13083 if (need_local_plte == 0)
13085 if (SetImageGray(image,exception) == MagickFalse)
13086 all_images_are_gray=MagickFalse;
13087 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13088 if (use_global_plte == 0)
13089 use_global_plte=mng_info->equal_palettes;
13090 need_local_plte=!mng_info->equal_palettes;
13093 if (GetNextImageInList(next_image) != (Image *) NULL)
13095 if (next_image->background_color.red !=
13096 next_image->next->background_color.red ||
13097 next_image->background_color.green !=
13098 next_image->next->background_color.green ||
13099 next_image->background_color.blue !=
13100 next_image->next->background_color.blue)
13101 mng_info->equal_backgrounds=MagickFalse;
13103 if (next_image->gamma != next_image->next->gamma)
13104 mng_info->equal_gammas=MagickFalse;
13106 if (next_image->rendering_intent !=
13107 next_image->next->rendering_intent)
13108 mng_info->equal_srgbs=MagickFalse;
13110 if ((next_image->units != next_image->next->units) ||
13111 (next_image->resolution.x != next_image->next->resolution.x) ||
13112 (next_image->resolution.y != next_image->next->resolution.y))
13113 mng_info->equal_physs=MagickFalse;
13115 if (mng_info->equal_chrms)
13117 if (next_image->chromaticity.red_primary.x !=
13118 next_image->next->chromaticity.red_primary.x ||
13119 next_image->chromaticity.red_primary.y !=
13120 next_image->next->chromaticity.red_primary.y ||
13121 next_image->chromaticity.green_primary.x !=
13122 next_image->next->chromaticity.green_primary.x ||
13123 next_image->chromaticity.green_primary.y !=
13124 next_image->next->chromaticity.green_primary.y ||
13125 next_image->chromaticity.blue_primary.x !=
13126 next_image->next->chromaticity.blue_primary.x ||
13127 next_image->chromaticity.blue_primary.y !=
13128 next_image->next->chromaticity.blue_primary.y ||
13129 next_image->chromaticity.white_point.x !=
13130 next_image->next->chromaticity.white_point.x ||
13131 next_image->chromaticity.white_point.y !=
13132 next_image->next->chromaticity.white_point.y)
13133 mng_info->equal_chrms=MagickFalse;
13137 next_image=GetNextImageInList(next_image);
13139 if (image_count < 2)
13141 mng_info->equal_backgrounds=MagickFalse;
13142 mng_info->equal_chrms=MagickFalse;
13143 mng_info->equal_gammas=MagickFalse;
13144 mng_info->equal_srgbs=MagickFalse;
13145 mng_info->equal_physs=MagickFalse;
13146 use_global_plte=MagickFalse;
13147 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13148 need_local_plte=MagickTrue;
13150 need_iterations=MagickFalse;
13153 if (mng_info->need_fram == MagickFalse)
13156 Only certain framing rates 100/n are exactly representable without
13157 the FRAM chunk but we'll allow some slop in VLC files
13159 if (final_delay == 0)
13161 if (need_iterations != MagickFalse)
13164 It's probably a GIF with loop; don't run it *too* fast.
13166 if (mng_info->adjoin)
13169 (void) ThrowMagickException(exception,GetMagickModule(),
13171 "input has zero delay between all frames; assuming",
13176 mng_info->ticks_per_second=0;
13178 if (final_delay != 0)
13179 mng_info->ticks_per_second=(png_uint_32)
13180 (image->ticks_per_second/final_delay);
13181 if (final_delay > 50)
13182 mng_info->ticks_per_second=2;
13184 if (final_delay > 75)
13185 mng_info->ticks_per_second=1;
13187 if (final_delay > 125)
13188 mng_info->need_fram=MagickTrue;
13190 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13191 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13192 (final_delay != 25) && (final_delay != 50) &&
13193 (final_delay != (size_t) image->ticks_per_second))
13194 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13197 if (mng_info->need_fram != MagickFalse)
13198 mng_info->ticks_per_second=image->ticks_per_second;
13200 If pseudocolor, we should also check to see if all the
13201 palettes are identical and write a global PLTE if they are.
13205 Write the MNG version 1.0 signature and MHDR chunk.
13207 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13208 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13209 PNGType(chunk,mng_MHDR);
13210 LogPNGChunk(logging,mng_MHDR,28L);
13211 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13212 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13213 PNGLong(chunk+12,mng_info->ticks_per_second);
13214 PNGLong(chunk+16,0L); /* layer count=unknown */
13215 PNGLong(chunk+20,0L); /* frame count=unknown */
13216 PNGLong(chunk+24,0L); /* play time=unknown */
13221 if (need_defi || mng_info->need_fram || use_global_plte)
13222 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13225 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13230 if (need_defi || mng_info->need_fram || use_global_plte)
13231 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13234 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13242 if (need_defi || mng_info->need_fram || use_global_plte)
13243 PNGLong(chunk+28,11L); /* simplicity=LC */
13246 PNGLong(chunk+28,9L); /* simplicity=VLC */
13251 if (need_defi || mng_info->need_fram || use_global_plte)
13252 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13255 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13258 (void) WriteBlob(image,32,chunk);
13259 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13260 option=GetImageOption(image_info,"mng:need-cacheoff");
13261 if (option != (const char *) NULL)
13267 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13269 PNGType(chunk,mng_nEED);
13270 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13271 (void) WriteBlobMSBULong(image,(size_t) length);
13272 LogPNGChunk(logging,mng_nEED,(size_t) length);
13274 (void) WriteBlob(image,length,chunk);
13275 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13277 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13278 (GetNextImageInList(image) != (Image *) NULL) &&
13279 (image->iterations != 1))
13282 Write MNG TERM chunk
13284 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13285 PNGType(chunk,mng_TERM);
13286 LogPNGChunk(logging,mng_TERM,10L);
13287 chunk[4]=3; /* repeat animation */
13288 chunk[5]=0; /* show last frame when done */
13289 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13290 final_delay/MagickMax(image->ticks_per_second,1)));
13292 if (image->iterations == 0)
13293 PNGLong(chunk+10,PNG_UINT_31_MAX);
13296 PNGLong(chunk+10,(png_uint_32) image->iterations);
13298 if (logging != MagickFalse)
13300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13301 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13302 final_delay/MagickMax(image->ticks_per_second,1)));
13304 if (image->iterations == 0)
13305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13306 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13310 " Image iterations: %.20g",(double) image->iterations);
13312 (void) WriteBlob(image,14,chunk);
13313 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13316 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13318 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13319 mng_info->equal_srgbs)
13322 Write MNG sRGB chunk
13324 (void) WriteBlobMSBULong(image,1L);
13325 PNGType(chunk,mng_sRGB);
13326 LogPNGChunk(logging,mng_sRGB,1L);
13328 if (image->rendering_intent != UndefinedIntent)
13329 chunk[4]=(unsigned char)
13330 Magick_RenderingIntent_to_PNG_RenderingIntent(
13331 (image->rendering_intent));
13334 chunk[4]=(unsigned char)
13335 Magick_RenderingIntent_to_PNG_RenderingIntent(
13336 (PerceptualIntent));
13338 (void) WriteBlob(image,5,chunk);
13339 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13340 mng_info->have_write_global_srgb=MagickTrue;
13345 if (image->gamma && mng_info->equal_gammas)
13348 Write MNG gAMA chunk
13350 (void) WriteBlobMSBULong(image,4L);
13351 PNGType(chunk,mng_gAMA);
13352 LogPNGChunk(logging,mng_gAMA,4L);
13353 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13354 (void) WriteBlob(image,8,chunk);
13355 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13356 mng_info->have_write_global_gama=MagickTrue;
13358 if (mng_info->equal_chrms)
13364 Write MNG cHRM chunk
13366 (void) WriteBlobMSBULong(image,32L);
13367 PNGType(chunk,mng_cHRM);
13368 LogPNGChunk(logging,mng_cHRM,32L);
13369 primary=image->chromaticity.white_point;
13370 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13371 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13372 primary=image->chromaticity.red_primary;
13373 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13374 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13375 primary=image->chromaticity.green_primary;
13376 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13377 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13378 primary=image->chromaticity.blue_primary;
13379 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13380 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13381 (void) WriteBlob(image,36,chunk);
13382 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13383 mng_info->have_write_global_chrm=MagickTrue;
13386 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13389 Write MNG pHYs chunk
13391 (void) WriteBlobMSBULong(image,9L);
13392 PNGType(chunk,mng_pHYs);
13393 LogPNGChunk(logging,mng_pHYs,9L);
13395 if (image->units == PixelsPerInchResolution)
13397 PNGLong(chunk+4,(png_uint_32)
13398 (image->resolution.x*100.0/2.54+0.5));
13400 PNGLong(chunk+8,(png_uint_32)
13401 (image->resolution.y*100.0/2.54+0.5));
13408 if (image->units == PixelsPerCentimeterResolution)
13410 PNGLong(chunk+4,(png_uint_32)
13411 (image->resolution.x*100.0+0.5));
13413 PNGLong(chunk+8,(png_uint_32)
13414 (image->resolution.y*100.0+0.5));
13421 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13422 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13426 (void) WriteBlob(image,13,chunk);
13427 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13430 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13431 or does not cover the entire frame.
13433 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13434 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13435 (image->page.width+image->page.x < mng_info->page.width))
13436 || (image->page.height && (image->page.height+image->page.y
13437 < mng_info->page.height))))
13439 (void) WriteBlobMSBULong(image,6L);
13440 PNGType(chunk,mng_BACK);
13441 LogPNGChunk(logging,mng_BACK,6L);
13442 red=ScaleQuantumToShort(image->background_color.red);
13443 green=ScaleQuantumToShort(image->background_color.green);
13444 blue=ScaleQuantumToShort(image->background_color.blue);
13445 PNGShort(chunk+4,red);
13446 PNGShort(chunk+6,green);
13447 PNGShort(chunk+8,blue);
13448 (void) WriteBlob(image,10,chunk);
13449 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13450 if (mng_info->equal_backgrounds)
13452 (void) WriteBlobMSBULong(image,6L);
13453 PNGType(chunk,mng_bKGD);
13454 LogPNGChunk(logging,mng_bKGD,6L);
13455 (void) WriteBlob(image,10,chunk);
13456 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13460 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13461 if ((need_local_plte == MagickFalse) &&
13462 (image->storage_class == PseudoClass) &&
13463 (all_images_are_gray == MagickFalse))
13469 Write MNG PLTE chunk
13471 data_length=3*image->colors;
13472 (void) WriteBlobMSBULong(image,data_length);
13473 PNGType(chunk,mng_PLTE);
13474 LogPNGChunk(logging,mng_PLTE,data_length);
13476 for (i=0; i < (ssize_t) image->colors; i++)
13478 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13479 image->colormap[i].red) & 0xff);
13480 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13481 image->colormap[i].green) & 0xff);
13482 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13483 image->colormap[i].blue) & 0xff);
13486 (void) WriteBlob(image,data_length+4,chunk);
13487 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13488 mng_info->have_write_global_plte=MagickTrue;
13494 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13495 defined(PNG_MNG_FEATURES_SUPPORTED)
13496 mng_info->equal_palettes=MagickFalse;
13500 if (mng_info->adjoin)
13502 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13503 defined(PNG_MNG_FEATURES_SUPPORTED)
13505 If we aren't using a global palette for the entire MNG, check to
13506 see if we can use one for two or more consecutive images.
13508 if (need_local_plte && use_global_plte && !all_images_are_gray)
13510 if (mng_info->IsPalette)
13513 When equal_palettes is true, this image has the same palette
13514 as the previous PseudoClass image
13516 mng_info->have_write_global_plte=mng_info->equal_palettes;
13517 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13518 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13521 Write MNG PLTE chunk
13526 data_length=3*image->colors;
13527 (void) WriteBlobMSBULong(image,data_length);
13528 PNGType(chunk,mng_PLTE);
13529 LogPNGChunk(logging,mng_PLTE,data_length);
13531 for (i=0; i < (ssize_t) image->colors; i++)
13533 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13534 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13535 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13538 (void) WriteBlob(image,data_length+4,chunk);
13539 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13540 (uInt) (data_length+4)));
13541 mng_info->have_write_global_plte=MagickTrue;
13545 mng_info->have_write_global_plte=MagickFalse;
13556 previous_x=mng_info->page.x;
13557 previous_y=mng_info->page.y;
13564 mng_info->page=image->page;
13565 if ((mng_info->page.x != previous_x) ||
13566 (mng_info->page.y != previous_y))
13568 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
13569 PNGType(chunk,mng_DEFI);
13570 LogPNGChunk(logging,mng_DEFI,12L);
13571 chunk[4]=0; /* object 0 MSB */
13572 chunk[5]=0; /* object 0 LSB */
13573 chunk[6]=0; /* visible */
13574 chunk[7]=0; /* abstract */
13575 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13576 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13577 (void) WriteBlob(image,16,chunk);
13578 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13583 mng_info->write_mng=write_mng;
13585 if ((int) image->dispose >= 3)
13586 mng_info->framing_mode=3;
13588 if (mng_info->need_fram && mng_info->adjoin &&
13589 ((image->delay != mng_info->delay) ||
13590 (mng_info->framing_mode != mng_info->old_framing_mode)))
13592 if (image->delay == mng_info->delay)
13595 Write a MNG FRAM chunk with the new framing mode.
13597 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
13598 PNGType(chunk,mng_FRAM);
13599 LogPNGChunk(logging,mng_FRAM,1L);
13600 chunk[4]=(unsigned char) mng_info->framing_mode;
13601 (void) WriteBlob(image,5,chunk);
13602 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13607 Write a MNG FRAM chunk with the delay.
13609 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13610 PNGType(chunk,mng_FRAM);
13611 LogPNGChunk(logging,mng_FRAM,10L);
13612 chunk[4]=(unsigned char) mng_info->framing_mode;
13613 chunk[5]=0; /* frame name separator (no name) */
13614 chunk[6]=2; /* flag for changing default delay */
13615 chunk[7]=0; /* flag for changing frame timeout */
13616 chunk[8]=0; /* flag for changing frame clipping */
13617 chunk[9]=0; /* flag for changing frame sync_id */
13618 PNGLong(chunk+10,(png_uint_32)
13619 ((mng_info->ticks_per_second*
13620 image->delay)/MagickMax(image->ticks_per_second,1)));
13621 (void) WriteBlob(image,14,chunk);
13622 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13623 mng_info->delay=(png_uint_32) image->delay;
13625 mng_info->old_framing_mode=mng_info->framing_mode;
13628 #if defined(JNG_SUPPORTED)
13629 if (image_info->compression == JPEGCompression)
13634 if (logging != MagickFalse)
13635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13636 " Writing JNG object.");
13637 /* To do: specify the desired alpha compression method. */
13638 write_info=CloneImageInfo(image_info);
13639 write_info->compression=UndefinedCompression;
13640 status=WriteOneJNGImage(mng_info,write_info,image,exception);
13641 write_info=DestroyImageInfo(write_info);
13646 if (logging != MagickFalse)
13647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13648 " Writing PNG object.");
13650 mng_info->need_blob = MagickFalse;
13651 mng_info->ping_preserve_colormap = MagickFalse;
13653 /* We don't want any ancillary chunks written */
13654 mng_info->ping_exclude_bKGD=MagickTrue;
13655 mng_info->ping_exclude_cHRM=MagickTrue;
13656 mng_info->ping_exclude_date=MagickTrue;
13657 mng_info->ping_exclude_EXIF=MagickTrue;
13658 mng_info->ping_exclude_gAMA=MagickTrue;
13659 mng_info->ping_exclude_iCCP=MagickTrue;
13660 /* mng_info->ping_exclude_iTXt=MagickTrue; */
13661 mng_info->ping_exclude_oFFs=MagickTrue;
13662 mng_info->ping_exclude_pHYs=MagickTrue;
13663 mng_info->ping_exclude_sRGB=MagickTrue;
13664 mng_info->ping_exclude_tEXt=MagickTrue;
13665 mng_info->ping_exclude_tRNS=MagickTrue;
13666 mng_info->ping_exclude_vpAg=MagickTrue;
13667 mng_info->ping_exclude_zCCP=MagickTrue;
13668 mng_info->ping_exclude_zTXt=MagickTrue;
13670 status=WriteOnePNGImage(mng_info,image_info,image,exception);
13673 if (status == MagickFalse)
13675 MngInfoFreeStruct(mng_info,&have_mng_structure);
13676 (void) CloseBlob(image);
13677 return(MagickFalse);
13679 (void) CatchImageException(image);
13680 if (GetNextImageInList(image) == (Image *) NULL)
13682 image=SyncNextImageInList(image);
13683 status=SetImageProgress(image,SaveImagesTag,scene++,
13684 GetImageListLength(image));
13686 if (status == MagickFalse)
13689 } while (mng_info->adjoin);
13693 while (GetPreviousImageInList(image) != (Image *) NULL)
13694 image=GetPreviousImageInList(image);
13696 Write the MEND chunk.
13698 (void) WriteBlobMSBULong(image,0x00000000L);
13699 PNGType(chunk,mng_MEND);
13700 LogPNGChunk(logging,mng_MEND,0L);
13701 (void) WriteBlob(image,4,chunk);
13702 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13705 Relinquish resources.
13707 (void) CloseBlob(image);
13708 MngInfoFreeStruct(mng_info,&have_mng_structure);
13710 if (logging != MagickFalse)
13711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13713 return(MagickTrue);
13715 #else /* PNG_LIBPNG_VER > 10011 */
13717 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13720 printf("Your PNG library is too old: You have libpng-%s\n",
13721 PNG_LIBPNG_VER_STRING);
13723 ThrowBinaryException(CoderError,"PNG library is too old",
13724 image_info->filename);
13727 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13729 return(WritePNGImage(image_info,image));
13731 #endif /* PNG_LIBPNG_VER > 10011 */