2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % https://imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colormap-private.h"
57 #include "MagickCore/colorspace.h"
58 #include "MagickCore/colorspace-private.h"
59 #include "MagickCore/constitute.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/geometry.h"
64 #include "MagickCore/histogram.h"
65 #include "MagickCore/image.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/layer.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/magick-private.h"
72 #include "MagickCore/memory_.h"
73 #include "MagickCore/memory-private.h"
74 #include "MagickCore/module.h"
75 #include "MagickCore/monitor.h"
76 #include "MagickCore/monitor-private.h"
77 #include "MagickCore/option.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/profile.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resource_.h"
84 #include "MagickCore/semaphore.h"
85 #include "MagickCore/quantum-private.h"
86 #include "MagickCore/static.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/transform.h"
91 #include "MagickCore/utility.h"
92 #if defined(MAGICKCORE_PNG_DELEGATE)
94 /* Suppress libpng pedantic warnings that were added in
95 * libpng-1.2.41 and libpng-1.4.0. If you are working on
96 * migration to libpng-1.5, remove these defines and then
97 * fix any code that generates warnings.
99 /* #define PNG_DEPRECATED Use of this function is deprecated */
100 /* #define PNG_USE_RESULT The result of this function must be checked */
101 /* #define PNG_NORETURN This function does not return */
102 /* #define PNG_ALLOCATED The result of the function is new memory */
103 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
105 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
106 #define PNG_PTR_NORETURN
111 /* ImageMagick differences */
112 #define first_scene scene
114 #if PNG_LIBPNG_VER > 10011
116 Optional declarations. Define or undefine them as you like.
118 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
121 Features under construction. Define these to work on them.
123 #undef MNG_OBJECT_BUFFERS
124 #undef MNG_BASI_SUPPORTED
125 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
126 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
127 #if defined(MAGICKCORE_JPEG_DELEGATE)
128 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
130 #if !defined(RGBColorMatchExact)
131 #define IsPNGColorEqual(color,target) \
132 (((color).red == (target).red) && \
133 ((color).green == (target).green) && \
134 ((color).blue == (target).blue))
137 /* Table of recognized sRGB ICC profiles */
138 struct sRGB_info_struct
145 const struct sRGB_info_struct sRGB_info[] =
147 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
148 { 3048, 0x3b8772b9UL, 0},
150 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
151 { 3052, 0x427ebb21UL, 1},
153 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
154 {60988, 0x306fd8aeUL, 0},
156 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
157 {60960, 0xbbef7812UL, 0},
159 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
160 { 3024, 0x5d5129ceUL, 1},
162 /* HP-Microsoft sRGB v2 perceptual */
163 { 3144, 0x182ea552UL, 0},
165 /* HP-Microsoft sRGB v2 media-relative */
166 { 3144, 0xf29e526dUL, 1},
168 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
169 { 524, 0xd4938c39UL, 0},
171 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
172 { 3212, 0x034af5a1UL, 0},
175 { 0, 0x00000000UL, 0},
178 /* Macros for left-bit-replication to ensure that pixels
179 * and PixelInfos all have the same image->depth, and for use
180 * in PNG8 quantization.
183 /* LBR01: Replicate top bit */
185 #define LBR01PacketRed(pixelpacket) \
186 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
189 #define LBR01PacketGreen(pixelpacket) \
190 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
193 #define LBR01PacketBlue(pixelpacket) \
194 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
197 #define LBR01PacketAlpha(pixelpacket) \
198 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
201 #define LBR01PacketRGB(pixelpacket) \
203 LBR01PacketRed((pixelpacket)); \
204 LBR01PacketGreen((pixelpacket)); \
205 LBR01PacketBlue((pixelpacket)); \
208 #define LBR01PacketRGBA(pixelpacket) \
210 LBR01PacketRGB((pixelpacket)); \
211 LBR01PacketAlpha((pixelpacket)); \
214 #define LBR01PixelRed(pixel) \
215 (SetPixelRed(image, \
216 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
217 0 : QuantumRange,(pixel)));
219 #define LBR01PixelGreen(pixel) \
220 (SetPixelGreen(image, \
221 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
222 0 : QuantumRange,(pixel)));
224 #define LBR01PixelBlue(pixel) \
225 (SetPixelBlue(image, \
226 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
227 0 : QuantumRange,(pixel)));
229 #define LBR01PixelAlpha(pixel) \
230 (SetPixelAlpha(image, \
231 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
232 0 : QuantumRange,(pixel)));
234 #define LBR01PixelRGB(pixel) \
236 LBR01PixelRed((pixel)); \
237 LBR01PixelGreen((pixel)); \
238 LBR01PixelBlue((pixel)); \
241 #define LBR01PixelRGBA(pixel) \
243 LBR01PixelRGB((pixel)); \
244 LBR01PixelAlpha((pixel)); \
247 /* LBR02: Replicate top 2 bits */
249 #define LBR02PacketRed(pixelpacket) \
251 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
252 (pixelpacket).red=ScaleCharToQuantum( \
253 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255 #define LBR02PacketGreen(pixelpacket) \
257 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
258 (pixelpacket).green=ScaleCharToQuantum( \
259 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261 #define LBR02PacketBlue(pixelpacket) \
263 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
264 (pixelpacket).blue=ScaleCharToQuantum( \
265 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267 #define LBR02PacketAlpha(pixelpacket) \
269 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
270 (pixelpacket).alpha=ScaleCharToQuantum( \
271 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
274 #define LBR02PacketRGB(pixelpacket) \
276 LBR02PacketRed((pixelpacket)); \
277 LBR02PacketGreen((pixelpacket)); \
278 LBR02PacketBlue((pixelpacket)); \
281 #define LBR02PacketRGBA(pixelpacket) \
283 LBR02PacketRGB((pixelpacket)); \
284 LBR02PacketAlpha((pixelpacket)); \
287 #define LBR02PixelRed(pixel) \
289 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
291 SetPixelRed(image, ScaleCharToQuantum( \
292 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
295 #define LBR02PixelGreen(pixel) \
297 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
299 SetPixelGreen(image, ScaleCharToQuantum( \
300 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
303 #define LBR02PixelBlue(pixel) \
305 unsigned char lbr_bits= \
306 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
307 SetPixelBlue(image, ScaleCharToQuantum( \
308 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
311 #define LBR02PixelAlpha(pixel) \
313 unsigned char lbr_bits= \
314 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
315 SetPixelAlpha(image, ScaleCharToQuantum( \
316 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
320 #define LBR02PixelRGB(pixel) \
322 LBR02PixelRed((pixel)); \
323 LBR02PixelGreen((pixel)); \
324 LBR02PixelBlue((pixel)); \
327 #define LBR02PixelRGBA(pixel) \
329 LBR02PixelRGB((pixel)); \
330 LBR02PixelAlpha((pixel)); \
333 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
334 PNG8 quantization) */
336 #define LBR03PacketRed(pixelpacket) \
338 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
339 (pixelpacket).red=ScaleCharToQuantum( \
340 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342 #define LBR03PacketGreen(pixelpacket) \
344 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
345 (pixelpacket).green=ScaleCharToQuantum( \
346 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348 #define LBR03PacketBlue(pixelpacket) \
350 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
351 (pixelpacket).blue=ScaleCharToQuantum( \
352 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
355 #define LBR03PacketRGB(pixelpacket) \
357 LBR03PacketRed((pixelpacket)); \
358 LBR03PacketGreen((pixelpacket)); \
359 LBR03PacketBlue((pixelpacket)); \
362 #define LBR03PixelRed(pixel) \
364 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
366 SetPixelRed(image, ScaleCharToQuantum( \
367 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
369 #define LBR03Green(pixel) \
371 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
373 SetPixelGreen(image, ScaleCharToQuantum( \
374 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
376 #define LBR03Blue(pixel) \
378 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
380 SetPixelBlue(image, ScaleCharToQuantum( \
381 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
384 #define LBR03RGB(pixel) \
386 LBR03PixelRed((pixel)); \
387 LBR03Green((pixel)); \
388 LBR03Blue((pixel)); \
391 /* LBR04: Replicate top 4 bits */
393 #define LBR04PacketRed(pixelpacket) \
395 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
396 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
398 #define LBR04PacketGreen(pixelpacket) \
400 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
401 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
403 #define LBR04PacketBlue(pixelpacket) \
405 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
406 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
408 #define LBR04PacketAlpha(pixelpacket) \
410 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
411 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
414 #define LBR04PacketRGB(pixelpacket) \
416 LBR04PacketRed((pixelpacket)); \
417 LBR04PacketGreen((pixelpacket)); \
418 LBR04PacketBlue((pixelpacket)); \
421 #define LBR04PacketRGBA(pixelpacket) \
423 LBR04PacketRGB((pixelpacket)); \
424 LBR04PacketAlpha((pixelpacket)); \
427 #define LBR04PixelRed(pixel) \
429 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
432 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
434 #define LBR04PixelGreen(pixel) \
436 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
438 SetPixelGreen(image,\
439 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
441 #define LBR04PixelBlue(pixel) \
443 unsigned char lbr_bits= \
444 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
446 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
448 #define LBR04PixelAlpha(pixel) \
450 unsigned char lbr_bits= \
451 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
452 SetPixelAlpha(image,\
453 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
456 #define LBR04PixelRGB(pixel) \
458 LBR04PixelRed((pixel)); \
459 LBR04PixelGreen((pixel)); \
460 LBR04PixelBlue((pixel)); \
463 #define LBR04PixelRGBA(pixel) \
465 LBR04PixelRGB((pixel)); \
466 LBR04PixelAlpha((pixel)); \
470 Establish thread safety.
471 setjmp/longjmp is claimed to be safe on these platforms:
472 setjmp/longjmp is alleged to be unsafe on these platforms:
474 #ifdef PNG_SETJMP_SUPPORTED
475 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
476 # define IMPNG_SETJMP_NOT_THREAD_SAFE
479 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
481 *ping_semaphore = (SemaphoreInfo *) NULL;
486 This temporary until I set up malloc'ed object attributes array.
487 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
490 #define MNG_MAX_OBJECTS 256
493 If this not defined, spec is interpreted strictly. If it is
494 defined, an attempt will be made to recover from some errors,
496 o global PLTE too short
501 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
502 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
503 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
504 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
505 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
506 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
507 will be enabled by default in libpng-1.2.0.
509 #ifdef PNG_MNG_FEATURES_SUPPORTED
510 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
511 # define PNG_READ_EMPTY_PLTE_SUPPORTED
513 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
514 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
519 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
520 This macro is only defined in libpng-1.0.3 and later.
521 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
523 #ifndef PNG_UINT_31_MAX
524 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
528 Constant strings for known chunk types. If you need to add a chunk,
529 add a string holding the name here. To make the code more
530 portable, we use ASCII numbers like this, not characters.
533 static const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
534 static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
535 static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
536 static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
537 static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
538 static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
539 static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
540 static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
541 static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
542 static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
543 static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
544 static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
545 static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
546 static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
547 static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
548 static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
549 static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
550 static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
551 static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
552 static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
553 static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
554 static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
555 static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
556 static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
557 static const png_byte mng_caNv[5]={ 99, 97, 78, 118, (png_byte) '\0'};
558 static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
559 static const png_byte mng_eXIf[5]={101, 88, 73, 102, (png_byte) '\0'};
560 static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
561 static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
562 static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
563 static const png_byte mng_orNT[5]={111, 114, 78, 84, (png_byte) '\0'};
564 static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
565 static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
566 static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
567 static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
568 static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
569 static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
571 #if defined(JNG_SUPPORTED)
572 static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
573 static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
574 static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
575 static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
576 static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
577 static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
581 /* Other known chunks that are not yet supported by ImageMagick: */
582 static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
583 static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
584 static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
585 static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
586 static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
587 static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
588 static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
591 typedef struct _MngBox
600 typedef struct _MngPair
607 #ifdef MNG_OBJECT_BUFFERS
608 typedef struct _MngBuffer
640 typedef struct _MngInfo
643 #ifdef MNG_OBJECT_BUFFERS
645 *ob[MNG_MAX_OBJECTS];
656 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
657 bytes_in_read_buffer,
663 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
664 defined(PNG_MNG_FEATURES_SUPPORTED)
676 have_saved_bkgd_index,
677 have_write_global_chrm,
678 have_write_global_gama,
679 have_write_global_plte,
680 have_write_global_srgb,
694 x_off[MNG_MAX_OBJECTS],
695 y_off[MNG_MAX_OBJECTS];
701 object_clip[MNG_MAX_OBJECTS];
704 /* These flags could be combined into one byte */
705 exists[MNG_MAX_OBJECTS],
706 frozen[MNG_MAX_OBJECTS],
708 invisible[MNG_MAX_OBJECTS],
709 viewable[MNG_MAX_OBJECTS];
721 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
739 global_x_pixels_per_unit,
740 global_y_pixels_per_unit,
750 global_phys_unit_type,
765 write_png_compression_level,
766 write_png_compression_strategy,
767 write_png_compression_filter,
774 #ifdef MNG_BASI_SUPPORTED
782 basi_compression_method,
784 basi_interlace_method,
807 /* Added at version 6.6.6-7 */
816 /* ping_exclude_iTXt, */
823 ping_exclude_zCCP, /* hex-encoded iCCP */
825 ping_preserve_colormap,
826 /* Added at version 6.8.5-7 */
828 /* Added at version 6.8.9-9 */
835 Forward declarations.
837 static MagickBooleanType
838 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
840 static MagickBooleanType
841 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
843 #if defined(JNG_SUPPORTED)
844 static MagickBooleanType
845 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
848 #if PNG_LIBPNG_VER > 10011
851 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
852 static MagickBooleanType
853 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
855 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
857 * This is true if the high byte and the next highest byte of
858 * each sample of the image, the colormap, and the background color
859 * are equal to each other. We check this by seeing if the samples
860 * are unchanged when we scale them down to 8 and back up to Quantum.
862 * We don't use the method GetImageDepth() because it doesn't check
863 * background and doesn't handle PseudoClass specially.
866 #define QuantumToCharToQuantumEqQuantum(quantum) \
867 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
870 ok_to_reduce=MagickFalse;
872 if (image->depth >= 16)
879 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
880 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
881 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
882 MagickTrue : MagickFalse;
884 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
888 for (indx=0; indx < (ssize_t) image->colors; indx++)
891 QuantumToCharToQuantumEqQuantum(
892 image->colormap[indx].red) &&
893 QuantumToCharToQuantumEqQuantum(
894 image->colormap[indx].green) &&
895 QuantumToCharToQuantumEqQuantum(
896 image->colormap[indx].blue)) ?
897 MagickTrue : MagickFalse;
899 if (ok_to_reduce == MagickFalse)
904 if ((ok_to_reduce != MagickFalse) &&
905 (image->storage_class != PseudoClass))
913 for (y=0; y < (ssize_t) image->rows; y++)
915 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
917 if (p == (const Quantum *) NULL)
919 ok_to_reduce = MagickFalse;
923 for (x=(ssize_t) image->columns-1; x >= 0; x--)
926 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
927 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
928 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
929 MagickTrue : MagickFalse;
931 if (ok_to_reduce == MagickFalse)
934 p+=GetPixelChannels(image);
941 if (ok_to_reduce != MagickFalse)
943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
944 " OK to reduce PNG bit depth to 8 without loss of info");
948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
949 " Not OK to reduce PNG bit depth to 8 without losing info");
955 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
957 static const char* PngColorTypeToString(const unsigned int color_type)
964 case PNG_COLOR_TYPE_GRAY:
967 case PNG_COLOR_TYPE_GRAY_ALPHA:
968 result = "Gray+Alpha";
970 case PNG_COLOR_TYPE_PALETTE:
973 case PNG_COLOR_TYPE_RGB:
976 case PNG_COLOR_TYPE_RGB_ALPHA:
977 result = "RGB+Alpha";
985 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
989 /* Convert to Exif orientations as defined in "Exchangeable image file
990 * format for digital still cameras: Exif Version 2.31",
991 * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
994 case TopLeftOrientation:
996 case TopRightOrientation:
998 case BottomRightOrientation:
1000 case BottomLeftOrientation:
1002 case LeftTopOrientation:
1004 case RightTopOrientation:
1006 case RightBottomOrientation:
1008 case LeftBottomOrientation:
1010 case UndefinedOrientation:
1015 static OrientationType
1016 Magick_Orientation_from_Exif_Orientation(const int orientation)
1018 switch (orientation)
1021 return TopLeftOrientation;
1023 return TopRightOrientation;
1025 return BottomRightOrientation;
1027 return BottomLeftOrientation;
1029 return LeftTopOrientation;
1031 return RightTopOrientation;
1033 return RightBottomOrientation;
1035 return LeftBottomOrientation;
1038 return UndefinedOrientation;
1043 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1047 case PerceptualIntent:
1050 case RelativeIntent:
1053 case SaturationIntent:
1056 case AbsoluteIntent:
1064 static RenderingIntent
1065 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1067 switch (ping_intent)
1070 return PerceptualIntent;
1073 return RelativeIntent;
1076 return SaturationIntent;
1079 return AbsoluteIntent;
1082 return UndefinedIntent;
1087 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1089 switch (ping_intent)
1092 return "Perceptual Intent";
1095 return "Relative Intent";
1098 return "Saturation Intent";
1101 return "Absolute Intent";
1104 return "Undefined Intent";
1109 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1111 switch (ping_colortype)
1129 return "UndefinedColorType";
1133 #endif /* PNG_LIBPNG_VER > 10011 */
1134 #endif /* MAGICKCORE_PNG_DELEGATE */
1137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1147 % IsMNG() returns MagickTrue if the image format type, identified by the
1148 % magick string, is MNG.
1150 % The format of the IsMNG method is:
1152 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1154 % A description of each parameter follows:
1156 % o magick: compare image format pattern against these bytes.
1158 % o length: Specifies the length of the magick string.
1162 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1165 return(MagickFalse);
1167 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1170 return(MagickFalse);
1174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184 % IsJNG() returns MagickTrue if the image format type, identified by the
1185 % magick string, is JNG.
1187 % The format of the IsJNG method is:
1189 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1191 % A description of each parameter follows:
1193 % o magick: compare image format pattern against these bytes.
1195 % o length: Specifies the length of the magick string.
1199 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1202 return(MagickFalse);
1204 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1207 return(MagickFalse);
1211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1221 % IsPNG() returns MagickTrue if the image format type, identified by the
1222 % magick string, is PNG.
1224 % The format of the IsPNG method is:
1226 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1228 % A description of each parameter follows:
1230 % o magick: compare image format pattern against these bytes.
1232 % o length: Specifies the length of the magick string.
1235 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1238 return(MagickFalse);
1240 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1243 return(MagickFalse);
1246 #if defined(MAGICKCORE_PNG_DELEGATE)
1247 #if defined(__cplusplus) || defined(c_plusplus)
1251 #if (PNG_LIBPNG_VER > 10011)
1252 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1257 assert(image != (Image *) NULL);
1258 assert(image->signature == MagickCoreSignature);
1259 buffer[0]=(unsigned char) (value >> 24);
1260 buffer[1]=(unsigned char) (value >> 16);
1261 buffer[2]=(unsigned char) (value >> 8);
1262 buffer[3]=(unsigned char) value;
1263 return((size_t) WriteBlob(image,4,buffer));
1266 static void PNGLong(png_bytep p,png_uint_32 value)
1268 *p++=(png_byte) ((value >> 24) & 0xff);
1269 *p++=(png_byte) ((value >> 16) & 0xff);
1270 *p++=(png_byte) ((value >> 8) & 0xff);
1271 *p++=(png_byte) (value & 0xff);
1274 static void PNGsLong(png_bytep p,png_int_32 value)
1276 *p++=(png_byte) ((value >> 24) & 0xff);
1277 *p++=(png_byte) ((value >> 16) & 0xff);
1278 *p++=(png_byte) ((value >> 8) & 0xff);
1279 *p++=(png_byte) (value & 0xff);
1282 static void PNGShort(png_bytep p,png_uint_16 value)
1284 *p++=(png_byte) ((value >> 8) & 0xff);
1285 *p++=(png_byte) (value & 0xff);
1288 static void PNGType(png_bytep p,const png_byte *type)
1290 (void) memcpy(p,type,4*sizeof(png_byte));
1293 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1296 if (logging != MagickFalse)
1297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1298 " Writing %c%c%c%c chunk, length: %.20g",
1299 type[0],type[1],type[2],type[3],(double) length);
1301 #endif /* PNG_LIBPNG_VER > 10011 */
1303 #if defined(__cplusplus) || defined(c_plusplus)
1307 #if PNG_LIBPNG_VER > 10011
1309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1313 % R e a d P N G I m a g e %
1317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1319 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1320 % Multiple-image Network Graphics (MNG) image file and returns it. It
1321 % allocates the memory necessary for the new Image structure and returns a
1322 % pointer to the new image or set of images.
1324 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1326 % The format of the ReadPNGImage method is:
1328 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1330 % A description of each parameter follows:
1332 % o image_info: the image info.
1334 % o exception: return any errors or warnings in this structure.
1336 % To do, more or less in chronological order (as of version 5.5.2,
1337 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1339 % Get 16-bit cheap transparency working.
1341 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1343 % Preserve all unknown and not-yet-handled known chunks found in input
1344 % PNG file and copy them into output PNG files according to the PNG
1347 % (At this point, PNG encoding should be in full MNG compliance)
1349 % Provide options for choice of background to use when the MNG BACK
1350 % chunk is not present or is not mandatory (i.e., leave transparent,
1351 % user specified, MNG BACK, PNG bKGD)
1353 % Implement LOOP/ENDL [done, but could do discretionary loops more
1354 % efficiently by linking in the duplicate frames.].
1356 % Decode and act on the MHDR simplicity profile (offer option to reject
1357 % files or attempt to process them anyway when the profile isn't LC or VLC).
1359 % Upgrade to full MNG without Delta-PNG.
1361 % o BACK [done a while ago except for background image ID]
1362 % o MOVE [done 15 May 1999]
1363 % o CLIP [done 15 May 1999]
1364 % o DISC [done 19 May 1999]
1365 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1366 % o SEEK [partially done 19 May 1999 (discard function only)]
1370 % o MNG-level tEXt/iTXt/zTXt
1375 % o iTXt (wait for libpng implementation).
1377 % Use the scene signature to discover when an identical scene is
1378 % being reused, and just point to the original image->exception instead
1379 % of storing another set of pixels. This not specific to MNG
1380 % but could be applied generally.
1382 % Upgrade to full MNG with Delta-PNG.
1384 % JNG tEXt/iTXt/zTXt
1386 % We will not attempt to read files containing the CgBI chunk.
1387 % They are really Xcode files meant for display on the iPhone.
1388 % These are not valid PNG files and it is impossible to recover
1389 % the original PNG from files that have been converted to Xcode-PNG,
1390 % since irretrievable loss of color data has occurred due to the
1391 % use of premultiplied alpha.
1394 #if defined(__cplusplus) || defined(c_plusplus)
1399 This the function that does the actual reading of data. It is
1400 the same as the one supplied in libpng, except that it receives the
1401 datastream from the ReadBlob() function instead of standard input.
1403 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1408 image=(Image *) png_get_io_ptr(png_ptr);
1414 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1415 if (check != length)
1418 msg[MagickPathExtent];
1420 (void) FormatLocaleString(msg,MagickPathExtent,
1421 "Expected %.20g bytes; found %.20g bytes",(double) length,
1423 png_warning(png_ptr,msg);
1424 png_error(png_ptr,"Read Exception");
1429 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1430 !defined(PNG_MNG_FEATURES_SUPPORTED)
1431 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1432 * older than libpng-1.0.3a, which was the first to allow the empty
1433 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1434 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1435 * encountered after an empty PLTE, so we have to look ahead for bKGD
1436 * chunks and remove them from the datastream that is passed to libpng,
1437 * and store their contents for later use.
1439 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1454 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1455 image=(Image *) mng_info->image;
1456 while (mng_info->bytes_in_read_buffer && length)
1458 data[i]=mng_info->read_buffer[i];
1459 mng_info->bytes_in_read_buffer--;
1465 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1467 if (check != length)
1468 png_error(png_ptr,"Read Exception");
1472 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1475 check=(png_size_t) ReadBlob(image,(size_t) length,
1476 (char *) mng_info->read_buffer);
1477 mng_info->read_buffer[4]=0;
1478 mng_info->bytes_in_read_buffer=4;
1479 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1480 mng_info->found_empty_plte=MagickTrue;
1481 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1483 mng_info->found_empty_plte=MagickFalse;
1484 mng_info->have_saved_bkgd_index=MagickFalse;
1488 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1491 check=(png_size_t) ReadBlob(image,(size_t) length,
1492 (char *) mng_info->read_buffer);
1493 mng_info->read_buffer[4]=0;
1494 mng_info->bytes_in_read_buffer=4;
1495 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1496 if (mng_info->found_empty_plte)
1499 Skip the bKGD data byte and CRC.
1502 ReadBlob(image,5,(char *) mng_info->read_buffer);
1503 check=(png_size_t) ReadBlob(image,(size_t) length,
1504 (char *) mng_info->read_buffer);
1505 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1506 mng_info->have_saved_bkgd_index=MagickTrue;
1507 mng_info->bytes_in_read_buffer=0;
1515 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1520 image=(Image *) png_get_io_ptr(png_ptr);
1526 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1528 if (check != length)
1529 png_error(png_ptr,"WriteBlob Failed");
1533 static void png_flush_data(png_structp png_ptr)
1538 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1539 static int PalettesAreEqual(Image *a,Image *b)
1544 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1545 return((int) MagickFalse);
1547 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1548 return((int) MagickFalse);
1550 if (a->colors != b->colors)
1551 return((int) MagickFalse);
1553 for (i=0; i < (ssize_t) a->colors; i++)
1555 if ((a->colormap[i].red != b->colormap[i].red) ||
1556 (a->colormap[i].green != b->colormap[i].green) ||
1557 (a->colormap[i].blue != b->colormap[i].blue))
1558 return((int) MagickFalse);
1561 return((int) MagickTrue);
1565 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1567 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1568 mng_info->exists[i] && !mng_info->frozen[i])
1570 #ifdef MNG_OBJECT_BUFFERS
1571 if (mng_info->ob[i] != (MngBuffer *) NULL)
1573 if (mng_info->ob[i]->reference_count > 0)
1574 mng_info->ob[i]->reference_count--;
1576 if (mng_info->ob[i]->reference_count == 0)
1578 if (mng_info->ob[i]->image != (Image *) NULL)
1579 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1581 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1584 mng_info->ob[i]=(MngBuffer *) NULL;
1586 mng_info->exists[i]=MagickFalse;
1587 mng_info->invisible[i]=MagickFalse;
1588 mng_info->viewable[i]=MagickFalse;
1589 mng_info->frozen[i]=MagickFalse;
1590 mng_info->x_off[i]=0;
1591 mng_info->y_off[i]=0;
1592 mng_info->object_clip[i].left=0;
1593 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1594 mng_info->object_clip[i].top=0;
1595 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1599 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1604 if (mng_info == (MngInfo *) NULL)
1605 return((MngInfo *) NULL);
1607 for (i=1; i < MNG_MAX_OBJECTS; i++)
1608 MngInfoDiscardObject(mng_info,i);
1610 mng_info->global_plte=(png_colorp)
1611 RelinquishMagickMemory(mng_info->global_plte);
1613 return((MngInfo *) RelinquishMagickMemory(mng_info));
1616 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1622 if (box.left < box2.left)
1625 if (box.top < box2.top)
1628 if (box.right > box2.right)
1629 box.right=box2.right;
1631 if (box.bottom > box2.bottom)
1632 box.bottom=box2.bottom;
1637 static long mng_get_long(unsigned char *p)
1639 return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1640 ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1643 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1650 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1652 box.left=mng_get_long(p);
1653 box.right=mng_get_long(&p[4]);
1654 box.top=mng_get_long(&p[8]);
1655 box.bottom=mng_get_long(&p[12]);
1656 if (delta_type != 0)
1658 box.left+=previous_box.left;
1659 box.right+=previous_box.right;
1660 box.top+=previous_box.top;
1661 box.bottom+=previous_box.bottom;
1667 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1674 Read two ssize_t's from CLON, MOVE or PAST chunk
1676 pair.a=mng_get_long(p);
1677 pair.b=mng_get_long(&p[4]);
1678 if (delta_type != 0)
1680 pair.a+=previous_pair.a;
1681 pair.b+=previous_pair.b;
1687 typedef struct _PNGErrorInfo
1696 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1707 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1708 image=error_info->image;
1709 exception=error_info->exception;
1711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1712 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1714 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1715 "`%s'",image->filename);
1717 #if (PNG_LIBPNG_VER < 10500)
1718 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1719 * are building with libpng-1.4.x and can be ignored.
1721 longjmp(ping->jmpbuf,1);
1723 png_longjmp(ping,1);
1727 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1738 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1739 png_error(ping, message);
1741 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1742 image=error_info->image;
1743 exception=error_info->exception;
1744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1745 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1747 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1748 message,"`%s'",image->filename);
1751 #ifdef PNG_USER_MEM_SUPPORTED
1752 #if PNG_LIBPNG_VER >= 10400
1753 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1755 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1759 return((png_voidp) AcquireMagickMemory((size_t) size));
1763 Free a pointer. It is removed from the list at the same time.
1765 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1768 ptr=RelinquishMagickMemory(ptr);
1769 return((png_free_ptr) NULL);
1773 #if defined(__cplusplus) || defined(c_plusplus)
1778 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1779 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1784 register unsigned char
1798 static const unsigned char
1799 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1800 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1801 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1802 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1803 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1807 extent=text[ii].text_length;
1808 /* look for newline */
1809 while ((*sp != '\n') && extent--)
1812 /* look for length */
1813 while (((*sp == '\0' || *sp == ' ' || *sp == '\n')) && extent--)
1818 png_warning(ping,"invalid profile length");
1819 return(MagickFalse);
1822 length=StringToLong(sp);
1824 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1825 " length: %lu",(unsigned long) length);
1827 while ((*sp != ' ' && *sp != '\n') && extent--)
1832 png_warning(ping,"missing profile length");
1833 return(MagickFalse);
1836 /* allocate space */
1839 png_warning(ping,"invalid profile length");
1840 return(MagickFalse);
1843 profile=BlobToStringInfo((const void *) NULL,length);
1845 if (profile == (StringInfo *) NULL)
1847 png_warning(ping, "unable to copy profile");
1848 return(MagickFalse);
1851 /* copy profile, skipping white space and column 1 "=" signs */
1852 dp=GetStringInfoDatum(profile);
1855 for (i=0; i < (ssize_t) nibbles; i++)
1857 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1861 png_warning(ping, "ran out of profile data");
1862 profile=DestroyStringInfo(profile);
1863 return(MagickFalse);
1869 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1872 (*dp++)+=unhex[(int) *sp++];
1875 We have already read "Raw profile type.
1877 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1878 profile=DestroyStringInfo(profile);
1880 if (image_info->verbose)
1881 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1886 static int PNGSetExifProfile(Image *image,png_size_t size,png_byte *data,
1887 ExceptionInfo *exception)
1901 profile=BlobToStringInfo((const void *) NULL,size+6);
1903 if (profile == (StringInfo *) NULL)
1905 (void) ThrowMagickException(exception,GetMagickModule(),
1906 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1910 p=GetStringInfoDatum(profile);
1912 /* Initialize profile with "Exif\0\0" */
1924 /* Skip first 6 bytes if "Exif\0\0" is
1925 already present by accident
1927 if (s[0] == 'E' && s[1] == 'x' && s[2] == 'i' &&
1928 s[3] == 'f' && s[4] == '\0' && s[5] == '\0')
1932 SetStringInfoLength(profile,size);
1933 p=GetStringInfoDatum(profile);
1937 /* copy chunk->data to profile */
1941 (void) SetImageProfile(image,"exif",profile,exception);
1943 profile=DestroyStringInfo(profile);
1948 #if defined(PNG_READ_eXIf_SUPPORTED)
1949 static void read_eXIf_chunk(Image *image,png_struct *ping,png_info *info,
1950 ExceptionInfo *exception)
1958 #if PNG_LIBPNG_VER > 10631
1959 if (png_get_eXIf_1(ping,info,&size,&data))
1960 (void) PNGSetExifProfile(image,size,data,exception);
1965 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1967 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1973 /* The unknown chunk structure contains the chunk data:
1978 Note that libpng has already taken care of the CRC handling.
1980 Returns one of the following:
1981 return(-n); chunk had an error
1982 return(0); did not recognize
1986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1987 " read_user_chunk: found %c%c%c%c chunk",
1988 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1990 if (chunk->name[0] == 101 &&
1991 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
1992 chunk->name[2] == 73 &&
1993 chunk-> name[3] == 102)
1995 /* process eXIf or exIf chunk */
2000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2001 " recognized eXIf chunk");
2003 image=(Image *) png_get_user_chunk_ptr(ping);
2005 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2007 return(PNGSetExifProfile(image,chunk->size,chunk->data,
2008 error_info->exception));
2012 if (chunk->name[0] == 111 &&
2013 chunk->name[1] == 114 &&
2014 chunk->name[2] == 78 &&
2015 chunk->name[3] == 84)
2017 /* recognized orNT */
2018 if (chunk->size != 1)
2019 return(-1); /* Error return */
2021 image=(Image *) png_get_user_chunk_ptr(ping);
2024 Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
2029 /* vpAg (deprecated, replaced by caNv) */
2030 if (chunk->name[0] == 118 &&
2031 chunk->name[1] == 112 &&
2032 chunk->name[2] == 65 &&
2033 chunk->name[3] == 103)
2035 /* recognized vpAg */
2037 if (chunk->size != 9)
2038 return(-1); /* Error return */
2040 if (chunk->data[8] != 0)
2041 return(0); /* ImageMagick requires pixel units */
2043 image=(Image *) png_get_user_chunk_ptr(ping);
2045 image->page.width=(size_t)mng_get_long(chunk->data);
2046 image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2052 if (chunk->name[0] == 99 &&
2053 chunk->name[1] == 97 &&
2054 chunk->name[2] == 78 &&
2055 chunk->name[3] == 118)
2057 /* recognized caNv */
2059 if (chunk->size != 16)
2060 return(-1); /* Error return */
2062 image=(Image *) png_get_user_chunk_ptr(ping);
2064 image->page.width=(size_t) mng_get_long(chunk->data);
2065 image->page.height=(size_t) mng_get_long(&chunk->data[4]);
2066 image->page.x=(ssize_t) ((int) mng_get_long(&chunk->data[8]));
2067 image->page.y=(ssize_t) ((int) mng_get_long(&chunk->data[12]));
2072 return(0); /* Did not recognize */
2074 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2076 #if defined(PNG_tIME_SUPPORTED)
2077 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2078 ExceptionInfo *exception)
2083 if (png_get_tIME(ping,info,&time))
2088 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2089 time->year,time->month,time->day,time->hour,time->minute,time->second);
2090 SetImageProperty(image,"png:tIME",timestamp,exception);
2096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2100 % R e a d O n e P N G I m a g e %
2104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2106 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2107 % (minus the 8-byte signature) and returns it. It allocates the memory
2108 % necessary for the new Image structure and returns a pointer to the new
2111 % The format of the ReadOnePNGImage method is:
2113 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2114 % ExceptionInfo *exception)
2116 % A description of each parameter follows:
2118 % o mng_info: Specifies a pointer to a MngInfo structure.
2120 % o image_info: the image info.
2122 % o exception: return any errors or warnings in this structure.
2125 static Image *ReadOnePNGImage(MngInfo *mng_info,
2126 const ImageInfo *image_info, ExceptionInfo *exception)
2128 /* Read one PNG image */
2130 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2143 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2153 ping_interlace_method,
2154 ping_compression_method,
2168 ping_found_sRGB_cHRM,
2173 *volatile pixel_info;
2205 *volatile quantum_info;
2208 *volatile quantum_scanline;
2214 register unsigned char
2234 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2235 png_byte unused_chunks[]=
2237 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2238 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2239 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2240 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2241 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2242 #if !defined(PNG_tIME_SUPPORTED)
2243 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2245 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2246 /* ignore the APNG chunks */
2247 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2248 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2249 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2254 /* Define these outside of the following "if logging()" block so they will
2255 * show in debuggers.
2258 (void) ConcatenateMagickString(im_vers,
2259 MagickLibVersionText,32);
2260 (void) ConcatenateMagickString(im_vers,
2261 MagickLibAddendum,32);
2264 (void) ConcatenateMagickString(libpng_vers,
2265 PNG_LIBPNG_VER_STRING,32);
2267 (void) ConcatenateMagickString(libpng_runv,
2268 png_get_libpng_ver(NULL),32);
2271 (void) ConcatenateMagickString(zlib_vers,
2274 (void) ConcatenateMagickString(zlib_runv,
2277 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2278 " Enter ReadOnePNGImage()\n"
2279 " IM version = %s\n"
2280 " Libpng version = %s",
2281 im_vers, libpng_vers);
2283 if (logging != MagickFalse)
2285 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2287 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2288 " running with %s", libpng_runv);
2290 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2291 " Zlib version = %s", zlib_vers);
2292 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2295 " running with %s", zlib_runv);
2299 #if (PNG_LIBPNG_VER < 10200)
2300 if (image_info->verbose)
2301 printf("Your PNG library (libpng-%s) is rather old.\n",
2302 PNG_LIBPNG_VER_STRING);
2305 #if (PNG_LIBPNG_VER >= 10400)
2306 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2307 if (image_info->verbose)
2309 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2310 PNG_LIBPNG_VER_STRING);
2311 printf("Please update it.\n");
2317 quantum_info = (QuantumInfo *) NULL;
2318 image=mng_info->image;
2320 if (logging != MagickFalse)
2322 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2323 " Before reading:\n"
2324 " image->alpha_trait=%d\n"
2325 " image->rendering_intent=%d\n"
2326 " image->colorspace=%d\n"
2328 (int) image->alpha_trait, (int) image->rendering_intent,
2329 (int) image->colorspace, image->gamma);
2332 Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2334 /* Set to an out-of-range color unless tRNS chunk is present */
2335 transparent_color.red=65537;
2336 transparent_color.green=65537;
2337 transparent_color.blue=65537;
2338 transparent_color.alpha=65537;
2343 num_raw_profiles = 0;
2345 ping_found_cHRM = MagickFalse;
2346 ping_found_gAMA = MagickFalse;
2347 ping_found_iCCP = MagickFalse;
2348 ping_found_sRGB = MagickFalse;
2349 ping_found_sRGB_cHRM = MagickFalse;
2350 ping_preserve_iCCP = MagickFalse;
2354 Allocate the PNG structures
2356 #ifdef PNG_USER_MEM_SUPPORTED
2357 error_info.image=image;
2358 error_info.exception=exception;
2359 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2360 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2361 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2363 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2364 MagickPNGErrorHandler,MagickPNGWarningHandler);
2366 if (ping == (png_struct *) NULL)
2367 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2369 ping_info=png_create_info_struct(ping);
2371 if (ping_info == (png_info *) NULL)
2373 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2374 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2377 end_info=png_create_info_struct(ping);
2379 if (end_info == (png_info *) NULL)
2381 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2382 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2385 pixel_info=(MemoryInfo *) NULL;
2386 quantum_scanline = (Quantum *) NULL;
2387 quantum_info = (QuantumInfo *) NULL;
2389 if (setjmp(png_jmpbuf(ping)))
2392 PNG image is corrupt.
2394 png_destroy_read_struct(&ping,&ping_info,&end_info);
2396 if (pixel_info != (MemoryInfo *) NULL)
2397 pixel_info=RelinquishVirtualMemory(pixel_info);
2399 if (quantum_info != (QuantumInfo *) NULL)
2400 quantum_info=DestroyQuantumInfo(quantum_info);
2402 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2404 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2405 UnlockSemaphoreInfo(ping_semaphore);
2408 if (logging != MagickFalse)
2409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2410 " exit ReadOnePNGImage() with error.");
2412 if (image != (Image *) NULL)
2413 image=DestroyImageList(image);
2417 /* { For navigation to end of SETJMP-protected block. Within this
2418 * block, use png_error() instead of Throwing an Exception, to ensure
2419 * that libpng is able to clean up, and that the semaphore is unlocked.
2422 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2423 LockSemaphoreInfo(ping_semaphore);
2426 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2427 /* Allow benign errors */
2428 png_set_benign_errors(ping, 1);
2431 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2436 /* Reject images with too many rows or columns */
2437 png_set_user_limits(ping,(png_uint_32) MagickMin(PNG_UINT_31_MAX,
2438 GetMagickResourceLimit(WidthResource)),(png_uint_32)
2439 MagickMin(PNG_UINT_31_MAX,GetMagickResourceLimit(HeightResource)));
2441 #if (PNG_LIBPNG_VER >= 10400)
2442 option=GetImageOption(image_info,"png:chunk-cache-max");
2443 if (option != (const char *) NULL)
2444 png_set_chunk_cache_max(ping,(png_uint_32) MagickMin(PNG_UINT_32_MAX,
2445 StringToLong(option)));
2447 png_set_chunk_cache_max(ping,32767);
2450 #if (PNG_LIBPNG_VER >= 10401)
2451 option=GetImageOption(image_info,"png:chunk-malloc-max");
2452 if (option != (const char *) NULL)
2453 png_set_chunk_malloc_max(ping,(png_alloc_size_t) MagickMin(PNG_SIZE_MAX,
2454 (size_t) StringToLong(option)));
2456 png_set_chunk_malloc_max(ping,(png_alloc_size_t) GetMaxMemoryRequest());
2459 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2462 Prepare PNG for reading.
2465 mng_info->image_found++;
2466 png_set_sig_bytes(ping,8);
2468 if (LocaleCompare(image_info->magick,"MNG") == 0)
2470 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2471 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2472 png_set_read_fn(ping,image,png_get_data);
2474 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2475 png_permit_empty_plte(ping,MagickTrue);
2476 png_set_read_fn(ping,image,png_get_data);
2478 mng_info->image=image;
2479 mng_info->bytes_in_read_buffer=0;
2480 mng_info->found_empty_plte=MagickFalse;
2481 mng_info->have_saved_bkgd_index=MagickFalse;
2482 png_set_read_fn(ping,mng_info,mng_get_data);
2488 png_set_read_fn(ping,image,png_get_data);
2494 value=GetImageOption(image_info,"png:ignore-crc");
2497 /* Turn off CRC checking while reading */
2498 png_set_crc_action(ping, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
2499 #ifdef PNG_IGNORE_ADLER32
2500 /* Turn off ADLER32 checking while reading */
2501 png_set_option(ping, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
2505 value=GetImageOption(image_info,"profile:skip");
2507 if (IsOptionMember("ICC",value) == MagickFalse)
2510 value=GetImageOption(image_info,"png:preserve-iCCP");
2513 value=GetImageArtifact(image,"png:preserve-iCCP");
2516 ping_preserve_iCCP=MagickTrue;
2518 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2519 /* Don't let libpng check for ICC/sRGB profile because we're going
2520 * to do that anyway. This feature was added at libpng-1.6.12.
2521 * If logging, go ahead and check and issue a warning as appropriate.
2523 if (logging == MagickFalse)
2524 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2527 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2530 png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2534 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2535 /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2536 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2537 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2539 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2541 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2542 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2543 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2544 (int)sizeof(unused_chunks)/5);
2545 /* Callback for other unknown chunks */
2546 png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2549 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2550 /* Disable new libpng-1.5.10 feature */
2551 png_set_check_for_invalid_index (ping, 0);
2554 #if (PNG_LIBPNG_VER < 10400)
2555 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2556 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2557 /* Disable thread-unsafe features of pnggccrd */
2558 if (png_access_version_number() >= 10200)
2560 png_uint_32 mmx_disable_mask=0;
2561 png_uint_32 asm_flags;
2563 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2564 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2565 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2566 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2567 asm_flags=png_get_asm_flags(ping);
2568 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2573 png_read_info(ping,ping_info);
2575 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2576 &ping_bit_depth,&ping_color_type,
2577 &ping_interlace_method,&ping_compression_method,
2578 &ping_filter_method);
2580 ping_file_depth = ping_bit_depth;
2582 /* Swap bytes if requested */
2583 if (ping_file_depth == 16)
2588 value=GetImageOption(image_info,"png:swap-bytes");
2591 value=GetImageArtifact(image,"png:swap-bytes");
2597 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2600 msg[MagickPathExtent];
2602 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2603 (int) ping_color_type);
2604 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2606 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2607 (int) ping_bit_depth);
2608 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2611 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2614 (void) png_get_bKGD(ping, ping_info, &ping_background);
2616 if (ping_bit_depth < 8)
2618 png_set_packing(ping);
2622 image->depth=ping_bit_depth;
2623 image->depth=GetImageQuantumDepth(image,MagickFalse);
2624 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2626 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2627 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2629 image->rendering_intent=UndefinedIntent;
2630 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2631 (void) memset(&image->chromaticity,0,
2632 sizeof(image->chromaticity));
2635 if (logging != MagickFalse)
2637 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2638 " PNG width: %.20g, height: %.20g\n"
2639 " PNG color_type: %d, bit_depth: %d\n"
2640 " PNG compression_method: %d\n"
2641 " PNG interlace_method: %d, filter_method: %d",
2642 (double) ping_width, (double) ping_height,
2643 ping_color_type, ping_bit_depth,
2644 ping_compression_method,
2645 ping_interlace_method,ping_filter_method);
2649 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2651 ping_found_iCCP=MagickTrue;
2652 if (logging != MagickFalse)
2653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2654 " Found PNG iCCP chunk.");
2657 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2659 ping_found_gAMA=MagickTrue;
2660 if (logging != MagickFalse)
2661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2662 " Found PNG gAMA chunk.");
2665 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2667 ping_found_cHRM=MagickTrue;
2668 if (logging != MagickFalse)
2669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2670 " Found PNG cHRM chunk.");
2673 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2676 ping_found_sRGB=MagickTrue;
2677 if (logging != MagickFalse)
2678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2679 " Found PNG sRGB chunk.");
2682 #ifdef PNG_READ_iCCP_SUPPORTED
2683 if (ping_found_iCCP !=MagickTrue &&
2684 ping_found_sRGB != MagickTrue &&
2685 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2687 ping_found_iCCP=MagickTrue;
2688 if (logging != MagickFalse)
2689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2690 " Found PNG iCCP chunk.");
2693 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2698 #if (PNG_LIBPNG_VER < 10500)
2712 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2715 if (profile_length != 0)
2720 if (logging != MagickFalse)
2721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2722 " Reading PNG iCCP chunk.");
2724 profile=BlobToStringInfo(info,(const size_t) profile_length);
2726 if (profile == (StringInfo *) NULL)
2728 png_warning(ping, "ICC profile is NULL");
2729 profile=DestroyStringInfo(profile);
2733 if (ping_preserve_iCCP == MagickFalse)
2746 profile_length=(png_uint_32) GetStringInfoLength(profile);
2748 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2750 if (profile_length == sRGB_info[icheck].len)
2754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2755 " Got a %lu-byte ICC profile (potentially sRGB)",
2756 (unsigned long) profile_length);
2758 data=GetStringInfoDatum(profile);
2759 profile_crc=crc32(0,data,profile_length);
2761 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2762 " with crc=%8x",(unsigned int) profile_crc);
2766 if (profile_crc == sRGB_info[icheck].crc)
2768 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2769 " It is sRGB with rendering intent = %s",
2770 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2771 sRGB_info[icheck].intent));
2772 if (image->rendering_intent==UndefinedIntent)
2774 image->rendering_intent=
2775 Magick_RenderingIntent_from_PNG_RenderingIntent(
2776 sRGB_info[icheck].intent);
2782 if (sRGB_info[icheck].len == 0)
2784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2785 " Got %lu-byte ICC profile not recognized as sRGB",
2786 (unsigned long) profile_length);
2787 (void) SetImageProfile(image,"icc",profile,exception);
2790 else /* Preserve-iCCP */
2792 (void) SetImageProfile(image,"icc",profile,exception);
2795 profile=DestroyStringInfo(profile);
2801 #if defined(PNG_READ_sRGB_SUPPORTED)
2803 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2806 if (png_get_sRGB(ping,ping_info,&intent))
2808 if (image->rendering_intent == UndefinedIntent)
2809 image->rendering_intent=
2810 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2812 if (logging != MagickFalse)
2813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2814 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2818 else if (mng_info->have_global_srgb)
2820 if (image->rendering_intent == UndefinedIntent)
2821 image->rendering_intent=
2822 Magick_RenderingIntent_from_PNG_RenderingIntent
2823 (mng_info->global_srgb_intent);
2830 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2831 if (mng_info->have_global_gama)
2832 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2834 if (png_get_gAMA(ping,ping_info,&file_gamma))
2836 image->gamma=(float) file_gamma;
2837 if (logging != MagickFalse)
2838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2839 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2843 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2845 if (mng_info->have_global_chrm != MagickFalse)
2847 (void) png_set_cHRM(ping,ping_info,
2848 mng_info->global_chrm.white_point.x,
2849 mng_info->global_chrm.white_point.y,
2850 mng_info->global_chrm.red_primary.x,
2851 mng_info->global_chrm.red_primary.y,
2852 mng_info->global_chrm.green_primary.x,
2853 mng_info->global_chrm.green_primary.y,
2854 mng_info->global_chrm.blue_primary.x,
2855 mng_info->global_chrm.blue_primary.y);
2859 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2861 (void) png_get_cHRM(ping,ping_info,
2862 &image->chromaticity.white_point.x,
2863 &image->chromaticity.white_point.y,
2864 &image->chromaticity.red_primary.x,
2865 &image->chromaticity.red_primary.y,
2866 &image->chromaticity.green_primary.x,
2867 &image->chromaticity.green_primary.y,
2868 &image->chromaticity.blue_primary.x,
2869 &image->chromaticity.blue_primary.y);
2871 ping_found_cHRM=MagickTrue;
2873 if (image->chromaticity.red_primary.x>0.6399f &&
2874 image->chromaticity.red_primary.x<0.6401f &&
2875 image->chromaticity.red_primary.y>0.3299f &&
2876 image->chromaticity.red_primary.y<0.3301f &&
2877 image->chromaticity.green_primary.x>0.2999f &&
2878 image->chromaticity.green_primary.x<0.3001f &&
2879 image->chromaticity.green_primary.y>0.5999f &&
2880 image->chromaticity.green_primary.y<0.6001f &&
2881 image->chromaticity.blue_primary.x>0.1499f &&
2882 image->chromaticity.blue_primary.x<0.1501f &&
2883 image->chromaticity.blue_primary.y>0.0599f &&
2884 image->chromaticity.blue_primary.y<0.0601f &&
2885 image->chromaticity.white_point.x>0.3126f &&
2886 image->chromaticity.white_point.x<0.3128f &&
2887 image->chromaticity.white_point.y>0.3289f &&
2888 image->chromaticity.white_point.y<0.3291f)
2889 ping_found_sRGB_cHRM=MagickTrue;
2892 if (image->rendering_intent != UndefinedIntent)
2894 if (ping_found_sRGB != MagickTrue &&
2895 (ping_found_gAMA != MagickTrue ||
2896 (image->gamma > .45 && image->gamma < .46)) &&
2897 (ping_found_cHRM != MagickTrue ||
2898 ping_found_sRGB_cHRM != MagickFalse) &&
2899 ping_found_iCCP != MagickTrue)
2901 png_set_sRGB(ping,ping_info,
2902 Magick_RenderingIntent_to_PNG_RenderingIntent
2903 (image->rendering_intent));
2904 file_gamma=0.45455f;
2905 ping_found_sRGB=MagickTrue;
2906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2907 " Setting sRGB as if in input");
2911 #if defined(PNG_oFFs_SUPPORTED)
2912 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2914 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2915 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2917 if (logging != MagickFalse)
2918 if (image->page.x || image->page.y)
2919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2920 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2921 image->page.x,(double) image->page.y);
2924 #if defined(PNG_pHYs_SUPPORTED)
2925 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2927 if (mng_info->have_global_phys)
2929 png_set_pHYs(ping,ping_info,
2930 mng_info->global_x_pixels_per_unit,
2931 mng_info->global_y_pixels_per_unit,
2932 mng_info->global_phys_unit_type);
2939 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2942 Set image resolution.
2944 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2946 image->resolution.x=(double) x_resolution;
2947 image->resolution.y=(double) y_resolution;
2949 if (unit_type == PNG_RESOLUTION_METER)
2951 image->units=PixelsPerCentimeterResolution;
2952 image->resolution.x=(double) x_resolution/100.0;
2953 image->resolution.y=(double) y_resolution/100.0;
2956 if (logging != MagickFalse)
2957 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2958 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2959 (double) x_resolution,(double) y_resolution,unit_type);
2963 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2968 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2970 if ((number_colors == 0) &&
2971 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2973 if (mng_info->global_plte_length)
2975 png_set_PLTE(ping,ping_info,mng_info->global_plte,
2976 (int) mng_info->global_plte_length);
2978 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2980 if (mng_info->global_trns_length)
2983 "global tRNS has more entries than global PLTE");
2987 png_set_tRNS(ping,ping_info,mng_info->global_trns,
2988 (int) mng_info->global_trns_length,NULL);
2991 #ifdef PNG_READ_bKGD_SUPPORTED
2993 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2994 mng_info->have_saved_bkgd_index ||
2996 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3001 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3002 if (mng_info->have_saved_bkgd_index)
3003 background.index=mng_info->saved_bkgd_index;
3005 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3006 background.index=ping_background->index;
3008 background.red=(png_uint_16)
3009 mng_info->global_plte[background.index].red;
3011 background.green=(png_uint_16)
3012 mng_info->global_plte[background.index].green;
3014 background.blue=(png_uint_16)
3015 mng_info->global_plte[background.index].blue;
3017 background.gray=(png_uint_16)
3018 mng_info->global_plte[background.index].green;
3020 png_set_bKGD(ping,ping_info,&background);
3025 png_error(ping,"No global PLTE in file");
3029 #ifdef PNG_READ_bKGD_SUPPORTED
3030 if (mng_info->have_global_bkgd &&
3031 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3032 image->background_color=mng_info->mng_global_bkgd;
3034 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3039 /* Set image background color.
3040 * Scale background components to 16-bit, then scale
3046 if (ping_file_depth == 1)
3049 else if (ping_file_depth == 2)
3052 else if (ping_file_depth == 4)
3055 if (ping_file_depth <= 8)
3058 ping_background->red *= bkgd_scale;
3059 ping_background->green *= bkgd_scale;
3060 ping_background->blue *= bkgd_scale;
3062 if (logging != MagickFalse)
3064 if (logging != MagickFalse)
3065 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3066 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3067 " bkgd_scale=%d. ping_background=(%d,%d,%d)",
3068 ping_background->red,ping_background->green,
3069 ping_background->blue,
3070 bkgd_scale,ping_background->red,
3071 ping_background->green,ping_background->blue);
3074 image->background_color.red=
3075 ScaleShortToQuantum(ping_background->red);
3077 image->background_color.green=
3078 ScaleShortToQuantum(ping_background->green);
3080 image->background_color.blue=
3081 ScaleShortToQuantum(ping_background->blue);
3083 image->background_color.alpha=OpaqueAlpha;
3085 if (logging != MagickFalse)
3086 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3087 " image->background_color=(%.20g,%.20g,%.20g).",
3088 (double) image->background_color.red,
3089 (double) image->background_color.green,
3090 (double) image->background_color.blue);
3092 #endif /* PNG_READ_bKGD_SUPPORTED */
3094 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3097 Image has a tRNS chunk.
3105 if (logging != MagickFalse)
3106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3107 " Reading PNG tRNS chunk.");
3109 max_sample = (int) ((one << ping_file_depth) - 1);
3111 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3112 (int)ping_trans_color->gray > max_sample) ||
3113 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3114 ((int)ping_trans_color->red > max_sample ||
3115 (int)ping_trans_color->green > max_sample ||
3116 (int)ping_trans_color->blue > max_sample)))
3118 if (logging != MagickFalse)
3119 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3120 " Ignoring PNG tRNS chunk with out-of-range sample.");
3121 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3122 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3123 image->alpha_trait=UndefinedPixelTrait;
3130 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3132 /* Scale transparent_color to short */
3133 transparent_color.red= scale_to_short*ping_trans_color->red;
3134 transparent_color.green= scale_to_short*ping_trans_color->green;
3135 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3136 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3138 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3140 if (logging != MagickFalse)
3142 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3143 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3144 (int) ping_trans_color->gray,(int) transparent_color.alpha);
3147 transparent_color.red=transparent_color.alpha;
3148 transparent_color.green=transparent_color.alpha;
3149 transparent_color.blue=transparent_color.alpha;
3153 #if defined(PNG_READ_sBIT_SUPPORTED)
3154 if (mng_info->have_global_sbit)
3156 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3157 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3160 num_passes=png_set_interlace_handling(ping);
3162 png_read_update_info(ping,ping_info);
3164 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3167 Initialize image structure.
3169 mng_info->image_box.left=0;
3170 mng_info->image_box.right=(ssize_t) ping_width;
3171 mng_info->image_box.top=0;
3172 mng_info->image_box.bottom=(ssize_t) ping_height;
3173 if (mng_info->mng_type == 0)
3175 mng_info->mng_width=ping_width;
3176 mng_info->mng_height=ping_height;
3177 mng_info->frame=mng_info->image_box;
3178 mng_info->clip=mng_info->image_box;
3183 image->page.y=mng_info->y_off[mng_info->object_id];
3186 image->compression=ZipCompression;
3187 image->columns=ping_width;
3188 image->rows=ping_height;
3190 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3191 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3194 image_gamma = image->gamma;
3196 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3197 " image->gamma=%f",(float) image_gamma);
3199 if (image_gamma > 0.75)
3201 /* Set image->rendering_intent to Undefined,
3202 * image->colorspace to GRAY, and reset image->chromaticity.
3204 image->intensity = Rec709LuminancePixelIntensityMethod;
3205 SetImageColorspace(image,LinearGRAYColorspace,exception);
3210 save_rendering_intent = image->rendering_intent;
3212 save_chromaticity = image->chromaticity;
3214 SetImageColorspace(image,GRAYColorspace,exception);
3215 image->rendering_intent = save_rendering_intent;
3216 image->chromaticity = save_chromaticity;
3219 image->gamma = image_gamma;
3224 image_gamma = image->gamma;
3226 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3227 " image->gamma=%f",(float) image_gamma);
3229 if (image_gamma > 0.75)
3231 /* Set image->rendering_intent to Undefined,
3232 * image->colorspace to GRAY, and reset image->chromaticity.
3234 image->intensity = Rec709LuminancePixelIntensityMethod;
3235 SetImageColorspace(image,RGBColorspace,exception);
3240 save_rendering_intent = image->rendering_intent;
3242 save_chromaticity = image->chromaticity;
3244 SetImageColorspace(image,sRGBColorspace,exception);
3245 image->rendering_intent = save_rendering_intent;
3246 image->chromaticity = save_chromaticity;
3249 image->gamma = image_gamma;
3252 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3253 " image->colorspace=%d",(int) image->colorspace);
3255 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3256 ((int) ping_bit_depth < 16 &&
3257 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3262 image->storage_class=PseudoClass;
3264 image->colors=one << ping_file_depth;
3265 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3266 if (image->colors > 256)
3269 if (image->colors > 65536L)
3270 image->colors=65536L;
3272 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3277 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3278 image->colors=(size_t) number_colors;
3280 if (logging != MagickFalse)
3281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3282 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3286 if (image->storage_class == PseudoClass)
3289 Initialize image colormap.
3291 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3292 png_error(ping,"Memory allocation failed");
3294 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3299 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3301 for (i=0; i < (ssize_t) number_colors; i++)
3303 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3304 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3305 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3308 for ( ; i < (ssize_t) image->colors; i++)
3310 image->colormap[i].red=0;
3311 image->colormap[i].green=0;
3312 image->colormap[i].blue=0;
3317 /* Set some properties for reporting by "identify" */
3320 msg[MagickPathExtent];
3322 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3323 ping_interlace_method in value */
3325 (void) FormatLocaleString(msg,MagickPathExtent,
3326 "%d, %d",(int) ping_width, (int) ping_height);
3327 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3329 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3330 (int) ping_file_depth);
3331 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3333 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3334 (int) ping_color_type,
3335 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3336 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3338 if (ping_interlace_method == 0)
3340 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3341 (int) ping_interlace_method);
3343 else if (ping_interlace_method == 1)
3345 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3346 (int) ping_interlace_method);
3350 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3351 (int) ping_interlace_method);
3353 (void) SetImageProperty(image,"png:IHDR.interlace_method",
3356 if (number_colors != 0)
3358 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3359 (int) number_colors);
3360 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3364 #if defined(PNG_tIME_SUPPORTED)
3365 read_tIME_chunk(image,ping,ping_info,exception);
3367 #if defined(PNG_READ_eXIf_SUPPORTED)
3368 read_eXIf_chunk(image,ping,ping_info,exception);
3373 Read image scanlines.
3375 if (image->delay != 0)
3376 mng_info->scenes_found++;
3378 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3379 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3380 (image_info->first_scene+image_info->number_scenes))))
3382 /* This happens later in non-ping decodes */
3383 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3384 image->storage_class=DirectClass;
3386 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3387 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3388 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3389 BlendPixelTrait : UndefinedPixelTrait;
3391 if (logging != MagickFalse)
3392 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3393 " Skipping PNG image data for scene %.20g",(double)
3394 mng_info->scenes_found-1);
3395 png_destroy_read_struct(&ping,&ping_info,&end_info);
3397 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3398 UnlockSemaphoreInfo(ping_semaphore);
3401 if (logging != MagickFalse)
3402 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3403 " exit ReadOnePNGImage().");
3408 if (logging != MagickFalse)
3409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3410 " Reading PNG IDAT chunk(s)");
3412 status=SetImageExtent(image,image->columns,image->rows,exception);
3413 if (status != MagickFalse)
3414 status=ResetImagePixels(image,exception);
3415 if (status == MagickFalse)
3417 png_destroy_read_struct(&ping,&ping_info,&end_info);
3418 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3419 UnlockSemaphoreInfo(ping_semaphore);
3421 return(DestroyImageList(image));
3425 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3426 sizeof(*ping_pixels));
3428 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3430 if (pixel_info == (MemoryInfo *) NULL)
3431 png_error(ping,"Memory allocation failed");
3432 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3434 if (logging != MagickFalse)
3435 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3436 " Converting PNG pixels to pixel packets");
3438 Convert PNG pixels to pixel packets.
3440 quantum_info=AcquireQuantumInfo(image_info,image);
3442 if (quantum_info == (QuantumInfo *) NULL)
3443 png_error(ping,"Failed to allocate quantum_info");
3445 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3450 found_transparent_pixel;
3452 found_transparent_pixel=MagickFalse;
3454 if (image->storage_class == DirectClass)
3456 for (pass=0; pass < num_passes; pass++)
3459 Convert image to DirectClass pixel packets.
3462 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3463 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3464 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3465 BlendPixelTrait : UndefinedPixelTrait;
3467 for (y=0; y < (ssize_t) image->rows; y++)
3470 row_offset=ping_rowbytes*y;
3475 png_read_row(ping,ping_pixels+row_offset,NULL);
3477 if (pass < num_passes-1)
3480 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3482 if (q == (Quantum *) NULL)
3485 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3486 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3487 GrayQuantum,ping_pixels+row_offset,exception);
3489 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3490 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3491 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3493 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3494 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3495 RGBAQuantum,ping_pixels+row_offset,exception);
3497 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3498 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3499 IndexQuantum,ping_pixels+row_offset,exception);
3501 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3502 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3503 RGBQuantum,ping_pixels+row_offset,exception);
3505 if (found_transparent_pixel == MagickFalse)
3507 /* Is there a transparent pixel in the row? */
3508 if (y== 0 && logging != MagickFalse)
3509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3510 " Looking for cheap transparent pixel");
3512 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3514 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3515 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3516 (GetPixelAlpha(image,q) != OpaqueAlpha))
3518 if (logging != MagickFalse)
3519 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3522 found_transparent_pixel = MagickTrue;
3525 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3526 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3527 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3528 transparent_color.red &&
3529 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3530 transparent_color.green &&
3531 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3532 transparent_color.blue))
3534 if (logging != MagickFalse)
3535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3537 found_transparent_pixel = MagickTrue;
3540 q+=GetPixelChannels(image);
3544 if (num_passes == 1)
3546 status=SetImageProgress(image,LoadImageTag,
3547 (MagickOffsetType) y, image->rows);
3549 if (status == MagickFalse)
3552 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3555 if (y < (long) image->rows)
3558 if (num_passes != 1)
3560 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3561 if (status == MagickFalse)
3567 else /* image->storage_class != DirectClass */
3569 for (pass=0; pass < num_passes; pass++)
3575 Convert grayscale image to PseudoClass pixel packets.
3577 if (logging != MagickFalse)
3578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3579 " Converting grayscale pixels to pixel packets");
3581 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3582 BlendPixelTrait : UndefinedPixelTrait;
3584 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3585 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3586 sizeof(*quantum_scanline));
3588 if (quantum_scanline == (Quantum *) NULL)
3589 png_error(ping,"Memory allocation failed");
3591 for (y=0; y < (ssize_t) image->rows; y++)
3597 row_offset=ping_rowbytes*y;
3602 png_read_row(ping,ping_pixels+row_offset,NULL);
3604 if (pass < num_passes-1)
3607 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3609 if (q == (Quantum *) NULL)
3612 p=ping_pixels+row_offset;
3615 switch (ping_bit_depth)
3620 if (ping_color_type == 4)
3621 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3625 alpha=ScaleCharToQuantum((unsigned char)*p++);
3627 SetPixelAlpha(image,alpha,q);
3629 if (alpha != OpaqueAlpha)
3630 found_transparent_pixel = MagickTrue;
3632 q+=GetPixelChannels(image);
3636 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3644 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3646 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3650 if (image->colors > 256)
3651 quantum=(((unsigned int) *p++) << 8);
3657 *r=ScaleShortToQuantum(quantum);
3660 if (ping_color_type == 4)
3662 if (image->colors > 256)
3663 quantum=(((unsigned int) *p++) << 8);
3669 alpha=ScaleShortToQuantum(quantum);
3670 SetPixelAlpha(image,alpha,q);
3672 if (alpha != OpaqueAlpha)
3673 found_transparent_pixel = MagickTrue;
3675 q+=GetPixelChannels(image);
3678 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3680 p++; /* strip low byte */
3682 if (ping_color_type == 4)
3684 SetPixelAlpha(image,*p++,q);
3686 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3687 found_transparent_pixel = MagickTrue;
3690 q+=GetPixelChannels(image);
3702 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3706 Transfer image scanline.
3710 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3712 if (q == (Quantum *) NULL)
3714 for (x=0; x < (ssize_t) image->columns; x++)
3716 ssize_t index=ConstrainColormapIndex(image,(ssize_t) *r,exception);
3717 SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
3718 SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
3719 SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
3720 SetPixelIndex(image,index,q);
3722 q+=GetPixelChannels(image);
3725 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3728 if (num_passes == 1)
3730 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3733 if (status == MagickFalse)
3737 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3738 if (y < (long) image->rows)
3740 if (num_passes != 1)
3742 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3744 if (status == MagickFalse)
3749 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3750 UndefinedPixelTrait;
3752 if (logging != MagickFalse)
3754 if (found_transparent_pixel != MagickFalse)
3755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3756 " Found transparent pixel");
3759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3760 " No transparent pixel was found");
3762 ping_color_type&=0x03;
3766 quantum_info=DestroyQuantumInfo(quantum_info);
3768 png_read_end(ping,end_info);
3770 if (logging != MagickFalse)
3772 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3773 " image->storage_class=%d\n",(int) image->storage_class);
3776 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3777 (ssize_t) image_info->first_scene && image->delay != 0)
3779 png_destroy_read_struct(&ping,&ping_info,&end_info);
3780 pixel_info=RelinquishVirtualMemory(pixel_info);
3782 (void) SetImageBackgroundColor(image,exception);
3783 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3784 UnlockSemaphoreInfo(ping_semaphore);
3786 if (logging != MagickFalse)
3787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3788 " exit ReadOnePNGImage() early.");
3792 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3798 Image has a transparent background.
3800 storage_class=image->storage_class;
3801 image->alpha_trait=BlendPixelTrait;
3803 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3805 if (storage_class == PseudoClass)
3807 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3809 for (x=0; x < ping_num_trans; x++)
3811 image->colormap[x].alpha_trait=BlendPixelTrait;
3812 image->colormap[x].alpha =
3813 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3817 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3819 for (x=0; x < (int) image->colors; x++)
3821 if (ScaleQuantumToShort(image->colormap[x].red) ==
3822 transparent_color.alpha)
3824 image->colormap[x].alpha_trait=BlendPixelTrait;
3825 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3829 (void) SyncImage(image,exception);
3832 #if 1 /* Should have already been done above, but glennrp problem P10
3837 for (y=0; y < (ssize_t) image->rows; y++)
3839 image->storage_class=storage_class;
3840 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3842 if (q == (Quantum *) NULL)
3846 /* Caution: on a Q8 build, this does not distinguish between
3847 * 16-bit colors that differ only in the low byte
3849 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3851 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3852 transparent_color.red &&
3853 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3854 transparent_color.green &&
3855 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3856 transparent_color.blue)
3858 SetPixelAlpha(image,TransparentAlpha,q);
3862 SetPixelAlpha(image,OpaqueAlpha,q);
3865 q+=GetPixelChannels(image);
3868 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3874 image->storage_class=DirectClass;
3877 for (j = 0; j < 2; j++)
3880 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3881 MagickTrue : MagickFalse;
3883 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3884 MagickTrue : MagickFalse;
3886 if (status != MagickFalse)
3887 for (i=0; i < (ssize_t) num_text; i++)
3889 /* Check for a profile */
3891 if (logging != MagickFalse)
3892 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3893 " Reading PNG text chunk");
3895 if (strlen(text[i].key) > 16 &&
3896 memcmp(text[i].key, "Raw profile type ",17) == 0)
3901 value=GetImageOption(image_info,"profile:skip");
3903 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3905 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3908 if (logging != MagickFalse)
3909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3910 " Read raw profile %s",text[i].key+17);
3914 if (logging != MagickFalse)
3915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3916 " Skipping raw profile %s",text[i].key+17);
3925 length=text[i].text_length;
3926 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3928 if (value == (char *) NULL)
3930 png_error(ping,"Memory allocation failed");
3934 (void) ConcatenateMagickString(value,text[i].text,length+2);
3936 /* Don't save "density" or "units" property if we have a pHYs
3939 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3940 (LocaleCompare(text[i].key,"density") != 0 &&
3941 LocaleCompare(text[i].key,"units") != 0))
3942 (void) SetImageProperty(image,text[i].key,value,exception);
3944 if (logging != MagickFalse)
3946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3949 (unsigned long) length,
3953 value=DestroyString(value);
3956 num_text_total += num_text;
3959 #ifdef MNG_OBJECT_BUFFERS
3961 Store the object if necessary.
3963 if (object_id && !mng_info->frozen[object_id])
3965 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3968 create a new object buffer.
3970 mng_info->ob[object_id]=(MngBuffer *)
3971 AcquireMagickMemory(sizeof(MngBuffer));
3973 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3975 mng_info->ob[object_id]->image=(Image *) NULL;
3976 mng_info->ob[object_id]->reference_count=1;
3980 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3981 mng_info->ob[object_id]->frozen)
3983 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3984 png_error(ping,"Memory allocation failed");
3986 if (mng_info->ob[object_id]->frozen)
3987 png_error(ping,"Cannot overwrite frozen MNG object buffer");
3993 if (mng_info->ob[object_id]->image != (Image *) NULL)
3994 mng_info->ob[object_id]->image=DestroyImage
3995 (mng_info->ob[object_id]->image);
3997 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4000 if (mng_info->ob[object_id]->image != (Image *) NULL)
4001 mng_info->ob[object_id]->image->file=(FILE *) NULL;
4004 png_error(ping, "Cloning image for object buffer failed");
4006 if (ping_width > 250000L || ping_height > 250000L)
4007 png_error(ping,"PNG Image dimensions are too large.");
4009 mng_info->ob[object_id]->width=ping_width;
4010 mng_info->ob[object_id]->height=ping_height;
4011 mng_info->ob[object_id]->color_type=ping_color_type;
4012 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4013 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4014 mng_info->ob[object_id]->compression_method=
4015 ping_compression_method;
4016 mng_info->ob[object_id]->filter_method=ping_filter_method;
4018 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4024 Copy the PLTE to the object buffer.
4026 png_get_PLTE(ping,ping_info,&plte,&number_colors);
4027 mng_info->ob[object_id]->plte_length=number_colors;
4029 for (i=0; i < number_colors; i++)
4031 mng_info->ob[object_id]->plte[i]=plte[i];
4036 mng_info->ob[object_id]->plte_length=0;
4041 /* Set image->alpha_trait to MagickTrue if the input colortype supports
4042 * alpha or if a valid tRNS chunk is present, no matter whether there
4043 * is actual transparency present.
4045 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4046 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4047 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4048 BlendPixelTrait : UndefinedPixelTrait;
4049 if (image->alpha_trait == BlendPixelTrait)
4050 (void) SetImageStorageClass(image,DirectClass,exception);
4052 #if 0 /* I'm not sure what's wrong here but it does not work. */
4053 if (image->alpha_trait != UndefinedPixelTrait)
4055 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4056 (void) SetImageType(image,GrayscaleAlphaType,exception);
4058 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4059 (void) SetImageType(image,PaletteAlphaType,exception);
4062 (void) SetImageType(image,TrueColorAlphaType,exception);
4067 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4068 (void) SetImageType(image,GrayscaleType,exception);
4070 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4071 (void) SetImageType(image,PaletteType,exception);
4074 (void) SetImageType(image,TrueColorType,exception);
4078 /* Set more properties for identify to retrieve */
4081 msg[MagickPathExtent];
4083 if (num_text_total != 0)
4085 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4086 (void) FormatLocaleString(msg,MagickPathExtent,
4087 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4088 (void) SetImageProperty(image,"png:text",msg,
4092 if (num_raw_profiles != 0)
4094 (void) FormatLocaleString(msg,MagickPathExtent,
4095 "%d were found", num_raw_profiles);
4096 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4101 if (ping_found_cHRM != MagickFalse)
4103 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4104 "chunk was found (see Chromaticity, above)");
4105 (void) SetImageProperty(image,"png:cHRM",msg,
4110 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4112 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4113 "chunk was found (see Background color, above)");
4114 (void) SetImageProperty(image,"png:bKGD",msg,
4118 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4121 #if defined(PNG_iCCP_SUPPORTED)
4123 if (ping_found_iCCP != MagickFalse)
4124 (void) SetImageProperty(image,"png:iCCP",msg,
4128 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4129 (void) SetImageProperty(image,"png:tRNS",msg,
4132 #if defined(PNG_sRGB_SUPPORTED)
4134 if (ping_found_sRGB != MagickFalse)
4136 (void) FormatLocaleString(msg,MagickPathExtent,
4139 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4140 (void) SetImageProperty(image,"png:sRGB",msg,
4146 if (ping_found_gAMA != MagickFalse)
4148 (void) FormatLocaleString(msg,MagickPathExtent,
4149 "gamma=%.8g (See Gamma, above)",
4151 (void) SetImageProperty(image,"png:gAMA",msg,
4155 #if defined(PNG_pHYs_SUPPORTED)
4157 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4159 (void) FormatLocaleString(msg,MagickPathExtent,
4160 "x_res=%.10g, y_res=%.10g, units=%d",
4161 (double) x_resolution,(double) y_resolution, unit_type);
4162 (void) SetImageProperty(image,"png:pHYs",msg,
4167 #if defined(PNG_oFFs_SUPPORTED)
4169 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4171 (void) FormatLocaleString(msg,MagickPathExtent,
4172 "x_off=%.20g, y_off=%.20g",
4173 (double) image->page.x,(double) image->page.y);
4174 (void) SetImageProperty(image,"png:oFFs",msg,
4179 #if defined(PNG_tIME_SUPPORTED)
4180 read_tIME_chunk(image,ping,end_info,exception);
4182 #if defined(PNG_READ_eXIf_SUPPORTED)
4183 read_eXIf_chunk(image,ping,end_info,exception);
4187 if ((image->page.width != 0 && image->page.width != image->columns) ||
4188 (image->page.height != 0 && image->page.height != image->rows) ||
4189 (image->page.x != 0 || image->page.y != 0))
4191 (void) FormatLocaleString(msg,MagickPathExtent,
4192 "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4193 (double) image->page.width,(double) image->page.height,
4194 (double) image->page.x,(double) image->page.y);
4195 (void) SetImageProperty(image,"png:caNv",msg,
4201 Relinquish resources.
4203 png_destroy_read_struct(&ping,&ping_info,&end_info);
4205 pixel_info=RelinquishVirtualMemory(pixel_info);
4207 if (logging != MagickFalse)
4208 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4209 " exit ReadOnePNGImage()");
4211 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4212 UnlockSemaphoreInfo(ping_semaphore);
4215 /* } for navigation to beginning of SETJMP-protected block, revert to
4216 * Throwing an Exception when an error occurs.
4221 /* end of reading one PNG image */
4224 static Image *ReadPNGImage(const ImageInfo *image_info,
4225 ExceptionInfo *exception)
4238 magic_number[MagickPathExtent];
4246 assert(image_info != (const ImageInfo *) NULL);
4247 assert(image_info->signature == MagickCoreSignature);
4249 if (image_info->debug != MagickFalse)
4250 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4251 image_info->filename);
4253 assert(exception != (ExceptionInfo *) NULL);
4254 assert(exception->signature == MagickCoreSignature);
4255 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4256 image=AcquireImage(image_info,exception);
4257 mng_info=(MngInfo *) NULL;
4258 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4260 if (status == MagickFalse)
4261 return(DestroyImageList(image));
4264 Verify PNG signature.
4266 count=ReadBlob(image,8,(unsigned char *) magic_number);
4268 if ((count < 8) || (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0))
4269 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4272 Verify that file size large enough to contain a PNG datastream.
4274 if (GetBlobSize(image) < 61)
4275 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4278 Allocate a MngInfo structure.
4280 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4282 if (mng_info == (MngInfo *) NULL)
4283 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4286 Initialize members of the MngInfo structure.
4288 (void) memset(mng_info,0,sizeof(MngInfo));
4289 mng_info->image=image;
4291 image=ReadOnePNGImage(mng_info,image_info,exception);
4292 mng_info=MngInfoFreeStruct(mng_info);
4294 if (image == (Image *) NULL)
4296 if (logging != MagickFalse)
4297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4298 "exit ReadPNGImage() with error");
4300 return((Image *) NULL);
4303 (void) CloseBlob(image);
4305 if ((image->columns == 0) || (image->rows == 0))
4307 if (logging != MagickFalse)
4308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4309 "exit ReadPNGImage() with error.");
4311 ThrowReaderException(CorruptImageError,"CorruptImage");
4314 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4315 ((image->gamma < .45) || (image->gamma > .46)) &&
4316 !(image->chromaticity.red_primary.x>0.6399f &&
4317 image->chromaticity.red_primary.x<0.6401f &&
4318 image->chromaticity.red_primary.y>0.3299f &&
4319 image->chromaticity.red_primary.y<0.3301f &&
4320 image->chromaticity.green_primary.x>0.2999f &&
4321 image->chromaticity.green_primary.x<0.3001f &&
4322 image->chromaticity.green_primary.y>0.5999f &&
4323 image->chromaticity.green_primary.y<0.6001f &&
4324 image->chromaticity.blue_primary.x>0.1499f &&
4325 image->chromaticity.blue_primary.x<0.1501f &&
4326 image->chromaticity.blue_primary.y>0.0599f &&
4327 image->chromaticity.blue_primary.y<0.0601f &&
4328 image->chromaticity.white_point.x>0.3126f &&
4329 image->chromaticity.white_point.x<0.3128f &&
4330 image->chromaticity.white_point.y>0.3289f &&
4331 image->chromaticity.white_point.y<0.3291f))
4333 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4334 "SetImageColorspace to RGBColorspace");
4335 SetImageColorspace(image,RGBColorspace,exception);
4338 if (logging != MagickFalse)
4340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4341 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4342 (double) image->page.width,(double) image->page.height,
4343 (double) image->page.x,(double) image->page.y);
4344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4345 " image->colorspace: %d", (int) image->colorspace);
4348 if (logging != MagickFalse)
4349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4356 #if defined(JNG_SUPPORTED)
4358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4362 % R e a d O n e J N G I m a g e %
4366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4368 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4369 % (minus the 8-byte signature) and returns it. It allocates the memory
4370 % necessary for the new Image structure and returns a pointer to the new
4373 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4375 % The format of the ReadOneJNGImage method is:
4377 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4378 % ExceptionInfo *exception)
4380 % A description of each parameter follows:
4382 % o mng_info: Specifies a pointer to a MngInfo structure.
4384 % o image_info: the image info.
4386 % o exception: return any errors or warnings in this structure.
4390 DestroyJNG(unsigned char *chunk,Image **color_image,
4391 ImageInfo **color_image_info,Image **alpha_image,
4392 ImageInfo **alpha_image_info)
4394 (void) RelinquishMagickMemory(chunk);
4395 if (color_image_info && *color_image_info)
4397 DestroyImageInfo(*color_image_info);
4398 *color_image_info = (ImageInfo *)NULL;
4400 if (alpha_image_info && *alpha_image_info)
4402 DestroyImageInfo(*alpha_image_info);
4403 *alpha_image_info = (ImageInfo *)NULL;
4405 if (color_image && *color_image)
4407 DestroyImage(*color_image);
4408 *color_image = (Image *)NULL;
4410 if (alpha_image && *alpha_image)
4412 DestroyImage(*alpha_image);
4413 *alpha_image = (Image *)NULL;
4416 static Image *ReadOneJNGImage(MngInfo *mng_info,
4417 const ImageInfo *image_info, ExceptionInfo *exception)
4444 jng_image_sample_depth,
4445 jng_image_compression_method,
4446 jng_image_interlace_method,
4447 jng_alpha_sample_depth,
4448 jng_alpha_compression_method,
4449 jng_alpha_filter_method,
4450 jng_alpha_interlace_method;
4452 register const Quantum
4462 register unsigned char
4472 jng_alpha_compression_method=0;
4473 jng_alpha_sample_depth=8;
4477 alpha_image=(Image *) NULL;
4478 color_image=(Image *) NULL;
4479 alpha_image_info=(ImageInfo *) NULL;
4480 color_image_info=(ImageInfo *) NULL;
4482 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4483 " Enter ReadOneJNGImage()");
4485 image=mng_info->image;
4487 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4490 Allocate next image structure.
4492 if (logging != MagickFalse)
4493 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4494 " AcquireNextImage()");
4496 AcquireNextImage(image_info,image,exception);
4498 if (GetNextImageInList(image) == (Image *) NULL)
4499 return(DestroyImageList(image));
4501 image=SyncNextImageInList(image);
4503 mng_info->image=image;
4506 Signature bytes have already been read.
4509 read_JSEP=MagickFalse;
4510 reading_idat=MagickFalse;
4514 type[MagickPathExtent];
4523 Read a new JNG chunk.
4525 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4526 2*GetBlobSize(image));
4528 if (status == MagickFalse)
4532 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4533 length=(size_t) ReadBlobMSBLong(image);
4534 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4536 if (logging != MagickFalse)
4537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4538 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4539 type[0],type[1],type[2],type[3],(double) length);
4541 if (length > PNG_UINT_31_MAX || count == 0)
4543 DestroyJNG(NULL,&color_image,&color_image_info,
4544 &alpha_image,&alpha_image_info);
4545 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4547 if (length > GetBlobSize(image))
4549 DestroyJNG(NULL,&color_image,&color_image_info,
4550 &alpha_image,&alpha_image_info);
4551 ThrowReaderException(CorruptImageError,
4552 "InsufficientImageDataInFile");
4556 chunk=(unsigned char *) NULL;
4560 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4562 if (chunk == (unsigned char *) NULL)
4564 DestroyJNG(NULL,&color_image,&color_image_info,
4565 &alpha_image,&alpha_image_info);
4566 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4569 for (i=0; i < (ssize_t) length; i++)
4574 c=ReadBlobByte(image);
4577 chunk[i]=(unsigned char) c;
4579 for ( ; i < (ssize_t) length; i++)
4585 (void) ReadBlobMSBLong(image); /* read crc word */
4587 if (memcmp(type,mng_JHDR,4) == 0)
4591 jng_width=(png_uint_32)mng_get_long(p);
4592 jng_height=(png_uint_32)mng_get_long(&p[4]);
4593 if ((jng_width == 0) || (jng_height == 0))
4595 DestroyJNG(chunk,&color_image,&color_image_info,
4596 &alpha_image,&alpha_image_info);
4597 ThrowReaderException(CorruptImageError,
4598 "NegativeOrZeroImageSize");
4600 jng_color_type=p[8];
4601 jng_image_sample_depth=p[9];
4602 jng_image_compression_method=p[10];
4603 jng_image_interlace_method=p[11];
4605 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4608 jng_alpha_sample_depth=p[12];
4609 jng_alpha_compression_method=p[13];
4610 jng_alpha_filter_method=p[14];
4611 jng_alpha_interlace_method=p[15];
4613 if (logging != MagickFalse)
4615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4616 " jng_width: %16lu, jng_height: %16lu\n"
4617 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4618 " jng_image_compression_method:%3d",
4619 (unsigned long) jng_width, (unsigned long) jng_height,
4620 jng_color_type, jng_image_sample_depth,
4621 jng_image_compression_method);
4623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4624 " jng_image_interlace_method: %3d"
4625 " jng_alpha_sample_depth: %3d",
4626 jng_image_interlace_method,
4627 jng_alpha_sample_depth);
4629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4630 " jng_alpha_compression_method:%3d\n"
4631 " jng_alpha_filter_method: %3d\n"
4632 " jng_alpha_interlace_method: %3d",
4633 jng_alpha_compression_method,
4634 jng_alpha_filter_method,
4635 jng_alpha_interlace_method);
4639 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4641 if ((jng_width > 65535) || (jng_height > 65535) ||
4642 (MagickSizeType) jng_width > GetMagickResourceLimit(WidthResource) ||
4643 (MagickSizeType) jng_height > GetMagickResourceLimit(HeightResource))
4645 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4646 " JNG width or height too large: (%lu x %lu)",
4647 (long) jng_width, (long) jng_height);
4648 DestroyJNG(chunk,&color_image,&color_image_info,
4649 &alpha_image,&alpha_image_info);
4650 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4657 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4658 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4659 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4662 o create color_image
4663 o open color_blob, attached to color_image
4664 o if (color type has alpha)
4665 open alpha_blob, attached to alpha_image
4668 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4670 if (color_image_info == (ImageInfo *) NULL)
4672 DestroyJNG(chunk,&color_image,&color_image_info,
4673 &alpha_image,&alpha_image_info);
4674 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4677 GetImageInfo(color_image_info);
4678 color_image=AcquireImage(color_image_info,exception);
4680 if (color_image == (Image *) NULL)
4682 DestroyJNG(chunk,&color_image,&color_image_info,
4683 &alpha_image,&alpha_image_info);
4684 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4687 if (logging != MagickFalse)
4688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4689 " Creating color_blob.");
4691 (void) AcquireUniqueFilename(color_image->filename);
4692 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4695 if (status == MagickFalse)
4697 DestroyJNG(chunk,&color_image,&color_image_info,
4698 &alpha_image,&alpha_image_info);
4699 return(DestroyImageList(image));
4702 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4704 alpha_image_info=(ImageInfo *)
4705 AcquireMagickMemory(sizeof(ImageInfo));
4707 if (alpha_image_info == (ImageInfo *) NULL)
4709 DestroyJNG(chunk,&color_image,&color_image_info,
4710 &alpha_image,&alpha_image_info);
4711 ThrowReaderException(ResourceLimitError,
4712 "MemoryAllocationFailed");
4715 GetImageInfo(alpha_image_info);
4716 alpha_image=AcquireImage(alpha_image_info,exception);
4718 if (alpha_image == (Image *) NULL)
4720 DestroyJNG(chunk,&color_image,&color_image_info,
4721 &alpha_image,&alpha_image_info);
4722 ThrowReaderException(ResourceLimitError,
4723 "MemoryAllocationFailed");
4726 if (logging != MagickFalse)
4727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4728 " Creating alpha_blob.");
4730 (void) AcquireUniqueFilename(alpha_image->filename);
4731 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4734 if (status == MagickFalse)
4736 DestroyJNG(chunk,&color_image,&color_image_info,
4737 &alpha_image,&alpha_image_info);
4738 return(DestroyImageList(image));
4741 if (jng_alpha_compression_method == 0)
4746 if (logging != MagickFalse)
4747 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4748 " Writing IHDR chunk to alpha_blob.");
4750 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4751 "\211PNG\r\n\032\n");
4753 (void) WriteBlobMSBULong(alpha_image,13L);
4754 PNGType(data,mng_IHDR);
4755 LogPNGChunk(logging,mng_IHDR,13L);
4756 PNGLong(data+4,jng_width);
4757 PNGLong(data+8,jng_height);
4758 data[12]=jng_alpha_sample_depth;
4759 data[13]=0; /* color_type gray */
4760 data[14]=0; /* compression method 0 */
4761 data[15]=0; /* filter_method 0 */
4762 data[16]=0; /* interlace_method 0 */
4763 (void) WriteBlob(alpha_image,17,data);
4764 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4767 reading_idat=MagickTrue;
4770 if (memcmp(type,mng_JDAT,4) == 0)
4772 /* Copy chunk to color_image->blob */
4774 if (logging != MagickFalse)
4775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4776 " Copying JDAT chunk data to color_blob.");
4778 if ((length != 0) && (color_image != (Image *) NULL))
4779 (void) WriteBlob(color_image,length,chunk);
4780 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4784 if (memcmp(type,mng_IDAT,4) == 0)
4789 /* Copy IDAT header and chunk data to alpha_image->blob */
4791 if (alpha_image != NULL && image_info->ping == MagickFalse)
4793 if (logging != MagickFalse)
4794 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4795 " Copying IDAT chunk data to alpha_blob.");
4797 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4798 PNGType(data,mng_IDAT);
4799 LogPNGChunk(logging,mng_IDAT,length);
4800 (void) WriteBlob(alpha_image,4,data);
4801 (void) WriteBlob(alpha_image,length,chunk);
4802 (void) WriteBlobMSBULong(alpha_image,
4803 crc32(crc32(0,data,4),chunk,(uInt) length));
4806 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4811 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4813 /* Copy chunk data to alpha_image->blob */
4815 if ((alpha_image != NULL) && (image_info->ping == MagickFalse) &&
4818 if (logging != MagickFalse)
4819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4820 " Copying JDAA chunk data to alpha_blob.");
4822 (void) WriteBlob(alpha_image,length,chunk);
4825 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4830 if (memcmp(type,mng_JSEP,4) == 0)
4832 read_JSEP=MagickTrue;
4834 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4839 if (memcmp(type,mng_bKGD,4) == 0)
4843 image->background_color.red=ScaleCharToQuantum(p[1]);
4844 image->background_color.green=image->background_color.red;
4845 image->background_color.blue=image->background_color.red;
4850 image->background_color.red=ScaleCharToQuantum(p[1]);
4851 image->background_color.green=ScaleCharToQuantum(p[3]);
4852 image->background_color.blue=ScaleCharToQuantum(p[5]);
4855 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4859 if (memcmp(type,mng_gAMA,4) == 0)
4862 image->gamma=((float) mng_get_long(p))*0.00001;
4864 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4868 if (memcmp(type,mng_cHRM,4) == 0)
4872 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4873 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4874 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4875 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4876 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4877 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4878 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4879 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4882 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4886 if (memcmp(type,mng_sRGB,4) == 0)
4890 image->rendering_intent=
4891 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4892 image->gamma=0.45455f;
4893 image->chromaticity.red_primary.x=0.6400f;
4894 image->chromaticity.red_primary.y=0.3300f;
4895 image->chromaticity.green_primary.x=0.3000f;
4896 image->chromaticity.green_primary.y=0.6000f;
4897 image->chromaticity.blue_primary.x=0.1500f;
4898 image->chromaticity.blue_primary.y=0.0600f;
4899 image->chromaticity.white_point.x=0.3127f;
4900 image->chromaticity.white_point.y=0.3290f;
4903 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4907 if (memcmp(type,mng_oFFs,4) == 0)
4911 image->page.x=(ssize_t) mng_get_long(p);
4912 image->page.y=(ssize_t) mng_get_long(&p[4]);
4914 if ((int) p[8] != 0)
4916 image->page.x/=10000;
4917 image->page.y/=10000;
4921 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4926 if (memcmp(type,mng_pHYs,4) == 0)
4930 image->resolution.x=(double) mng_get_long(p);
4931 image->resolution.y=(double) mng_get_long(&p[4]);
4932 if ((int) p[8] == PNG_RESOLUTION_METER)
4934 image->units=PixelsPerCentimeterResolution;
4935 image->resolution.x=image->resolution.x/100.0f;
4936 image->resolution.y=image->resolution.y/100.0f;
4940 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4945 if (memcmp(type,mng_iCCP,4) == 0)
4948 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4954 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4956 if (memcmp(type,mng_IEND,4))
4966 Finish up reading image data:
4968 o read main image from color_blob.
4972 o if (color_type has alpha)
4973 if alpha_encoding is PNG
4974 read secondary image from alpha_blob via ReadPNG
4975 if alpha_encoding is JPEG
4976 read secondary image from alpha_blob via ReadJPEG
4980 o copy intensity of secondary image into
4981 alpha samples of main image.
4983 o destroy the secondary image.
4986 if (color_image_info == (ImageInfo *) NULL)
4988 assert(color_image == (Image *) NULL);
4989 assert(alpha_image == (Image *) NULL);
4990 if (color_image != (Image *) NULL)
4991 color_image=DestroyImageList(color_image);
4992 return(DestroyImageList(image));
4995 if (color_image == (Image *) NULL)
4997 assert(alpha_image == (Image *) NULL);
4998 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5001 (void) SeekBlob(color_image,0,SEEK_SET);
5003 if (logging != MagickFalse)
5004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5005 " Reading jng_image from color_blob.");
5007 assert(color_image_info != (ImageInfo *) NULL);
5008 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,
5009 "jpeg:%s",color_image->filename);
5011 color_image_info->ping=MagickFalse; /* To do: avoid this */
5012 jng_image=ReadImage(color_image_info,exception);
5014 (void) RelinquishUniqueFileResource(color_image->filename);
5015 color_image=DestroyImage(color_image);
5016 color_image_info=DestroyImageInfo(color_image_info);
5018 if (jng_image == (Image *) NULL)
5020 DestroyJNG(NULL,NULL,NULL,&alpha_image,&alpha_image_info);
5021 return(DestroyImageList(image));
5025 if (logging != MagickFalse)
5026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5027 " Copying jng_image pixels to main image.");
5029 image->rows=jng_height;
5030 image->columns=jng_width;
5032 status=SetImageExtent(image,image->columns,image->rows,exception);
5033 if (status == MagickFalse)
5035 DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5037 jng_image=DestroyImageList(jng_image);
5038 return(DestroyImageList(image));
5040 if ((image->columns != jng_image->columns) ||
5041 (image->rows != jng_image->rows))
5043 DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5045 jng_image=DestroyImageList(jng_image);
5046 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5048 for (y=0; y < (ssize_t) image->rows; y++)
5050 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5051 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5052 if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5054 for (x=(ssize_t) image->columns; x != 0; x--)
5056 SetPixelRed(image,GetPixelRed(jng_image,s),q);
5057 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5058 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5059 q+=GetPixelChannels(image);
5060 s+=GetPixelChannels(jng_image);
5063 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5067 jng_image=DestroyImage(jng_image);
5069 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
5071 if (jng_alpha_compression_method == 0)
5075 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5076 PNGType(data,mng_IEND);
5077 LogPNGChunk(logging,mng_IEND,0L);
5078 (void) WriteBlob(alpha_image,4,data);
5079 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5082 (void) CloseBlob(alpha_image);
5084 if (logging != MagickFalse)
5085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5086 " Reading alpha from alpha_blob.");
5088 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5089 "%s",alpha_image->filename);
5091 jng_image=ReadImage(alpha_image_info,exception);
5093 if (jng_image != (Image *) NULL)
5094 for (y=0; y < (ssize_t) image->rows; y++)
5096 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5097 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5098 if ((s == (const Quantum *) NULL) || (q == (Quantum *) NULL))
5101 if (image->alpha_trait != UndefinedPixelTrait)
5102 for (x=(ssize_t) image->columns; x != 0; x--)
5104 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5105 q+=GetPixelChannels(image);
5106 s+=GetPixelChannels(jng_image);
5110 for (x=(ssize_t) image->columns; x != 0; x--)
5112 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5113 if (GetPixelAlpha(image,q) != OpaqueAlpha)
5114 image->alpha_trait=BlendPixelTrait;
5115 q+=GetPixelChannels(image);
5116 s+=GetPixelChannels(jng_image);
5119 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5122 (void) RelinquishUniqueFileResource(alpha_image->filename);
5123 alpha_image=DestroyImage(alpha_image);
5124 alpha_image_info=DestroyImageInfo(alpha_image_info);
5125 if (jng_image != (Image *) NULL)
5126 jng_image=DestroyImage(jng_image);
5129 /* Read the JNG image. */
5131 if (mng_info->mng_type == 0)
5133 mng_info->mng_width=jng_width;
5134 mng_info->mng_height=jng_height;
5137 if (image->page.width == 0 && image->page.height == 0)
5139 image->page.width=jng_width;
5140 image->page.height=jng_height;
5143 if (image->page.x == 0 && image->page.y == 0)
5145 image->page.x=mng_info->x_off[mng_info->object_id];
5146 image->page.y=mng_info->y_off[mng_info->object_id];
5151 image->page.y=mng_info->y_off[mng_info->object_id];
5154 mng_info->image_found++;
5155 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5156 2*GetBlobSize(image));
5158 if (status == MagickFalse)
5159 return(DestroyImageList(image));
5161 if (logging != MagickFalse)
5162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5163 " exit ReadOneJNGImage()");
5169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5173 % R e a d J N G I m a g e %
5177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5179 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5180 % (including the 8-byte signature) and returns it. It allocates the memory
5181 % necessary for the new Image structure and returns a pointer to the new
5184 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
5186 % The format of the ReadJNGImage method is:
5188 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5191 % A description of each parameter follows:
5193 % o image_info: the image info.
5195 % o exception: return any errors or warnings in this structure.
5199 static Image *ReadJNGImage(const ImageInfo *image_info,
5200 ExceptionInfo *exception)
5213 magic_number[MagickPathExtent];
5221 assert(image_info != (const ImageInfo *) NULL);
5222 assert(image_info->signature == MagickCoreSignature);
5223 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5224 image_info->filename);
5225 assert(exception != (ExceptionInfo *) NULL);
5226 assert(exception->signature == MagickCoreSignature);
5227 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5228 image=AcquireImage(image_info,exception);
5229 mng_info=(MngInfo *) NULL;
5230 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5232 if (status == MagickFalse)
5233 return(DestroyImageList(image));
5235 if (LocaleCompare(image_info->magick,"JNG") != 0)
5236 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5238 /* Verify JNG signature. */
5240 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5242 if ((count < 8) || (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0))
5243 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5246 Verify that file size large enough to contain a JNG datastream.
5248 if (GetBlobSize(image) < 147)
5249 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5251 /* Allocate a MngInfo structure. */
5253 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5255 if (mng_info == (MngInfo *) NULL)
5256 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5258 /* Initialize members of the MngInfo structure. */
5260 (void) memset(mng_info,0,sizeof(MngInfo));
5262 mng_info->image=image;
5263 image=ReadOneJNGImage(mng_info,image_info,exception);
5264 mng_info=MngInfoFreeStruct(mng_info);
5266 if (image == (Image *) NULL)
5268 if (logging != MagickFalse)
5269 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5270 "exit ReadJNGImage() with error");
5272 return((Image *) NULL);
5274 (void) CloseBlob(image);
5276 if (image->columns == 0 || image->rows == 0)
5278 if (logging != MagickFalse)
5279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5280 "exit ReadJNGImage() with error");
5282 ThrowReaderException(CorruptImageError,"CorruptImage");
5285 if (logging != MagickFalse)
5286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5292 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5293 ExceptionInfo *exception)
5296 page_geometry[MagickPathExtent];
5324 #if defined(MNG_INSERT_LAYERS)
5326 mng_background_color;
5329 register unsigned char
5344 #if defined(MNG_INSERT_LAYERS)
5349 volatile unsigned int
5350 #ifdef MNG_OBJECT_BUFFERS
5351 mng_background_object=0,
5353 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5356 default_frame_timeout,
5358 #if defined(MNG_INSERT_LAYERS)
5364 /* These delays are all measured in image ticks_per_second,
5365 * not in MNG ticks_per_second
5368 default_frame_delay,
5372 #if defined(MNG_INSERT_LAYERS)
5381 previous_fb.bottom=0;
5383 previous_fb.right=0;
5385 default_fb.bottom=0;
5389 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5390 " Enter ReadOneMNGImage()");
5392 image=mng_info->image;
5394 if (LocaleCompare(image_info->magick,"MNG") == 0)
5397 magic_number[MagickPathExtent];
5399 /* Verify MNG signature. */
5400 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5401 if ((count < 8) || (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0))
5402 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5404 /* Initialize some nonzero members of the MngInfo structure. */
5405 for (i=0; i < MNG_MAX_OBJECTS; i++)
5407 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5408 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5410 mng_info->exists[0]=MagickTrue;
5414 first_mng_object=MagickTrue;
5416 #if defined(MNG_INSERT_LAYERS)
5417 insert_layers=MagickFalse; /* should be False during convert or mogrify */
5419 default_frame_delay=0;
5420 default_frame_timeout=0;
5423 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5425 skip_to_iend=MagickFalse;
5426 term_chunk_found=MagickFalse;
5427 mng_info->framing_mode=1;
5428 #if defined(MNG_INSERT_LAYERS)
5429 mandatory_back=MagickFalse;
5431 #if defined(MNG_INSERT_LAYERS)
5432 mng_background_color=image->background_color;
5434 default_fb=mng_info->frame;
5435 previous_fb=mng_info->frame;
5439 type[MagickPathExtent];
5441 if (LocaleCompare(image_info->magick,"MNG") == 0)
5450 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5451 length=(size_t) ReadBlobMSBLong(image);
5452 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5454 if (logging != MagickFalse)
5455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5456 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5457 type[0],type[1],type[2],type[3],(double) length);
5459 if ((length > PNG_UINT_31_MAX) || (length > GetBlobSize(image)) ||
5461 ThrowReaderException(CorruptImageError,"CorruptImage");
5464 chunk=(unsigned char *) NULL;
5468 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5470 if (chunk == (unsigned char *) NULL)
5471 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5473 for (i=0; i < (ssize_t) length; i++)
5478 c=ReadBlobByte(image);
5481 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5482 ThrowReaderException(CorruptImageError,
5483 "InsufficientImageDataInFile");
5485 chunk[i]=(unsigned char) c;
5491 (void) ReadBlobMSBLong(image); /* read crc word */
5493 #if !defined(JNG_SUPPORTED)
5494 if (memcmp(type,mng_JHDR,4) == 0)
5496 skip_to_iend=MagickTrue;
5498 if (mng_info->jhdr_warning == 0)
5499 (void) ThrowMagickException(exception,GetMagickModule(),
5500 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5502 mng_info->jhdr_warning++;
5505 if (memcmp(type,mng_DHDR,4) == 0)
5507 skip_to_iend=MagickTrue;
5509 if (mng_info->dhdr_warning == 0)
5510 (void) ThrowMagickException(exception,GetMagickModule(),
5511 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5513 mng_info->dhdr_warning++;
5515 if (memcmp(type,mng_MEND,4) == 0)
5517 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5523 if (memcmp(type,mng_IEND,4) == 0)
5524 skip_to_iend=MagickFalse;
5526 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5528 if (logging != MagickFalse)
5529 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5535 if (memcmp(type,mng_MHDR,4) == 0)
5539 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5540 ThrowReaderException(CorruptImageError,"CorruptImage");
5543 mng_info->mng_width=(unsigned long)mng_get_long(p);
5544 mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5546 if (logging != MagickFalse)
5548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5549 " MNG width: %.20g",(double) mng_info->mng_width);
5550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5551 " MNG height: %.20g",(double) mng_info->mng_height);
5555 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5557 if (mng_info->ticks_per_second == 0)
5558 default_frame_delay=0;
5561 default_frame_delay=1UL*image->ticks_per_second/
5562 mng_info->ticks_per_second;
5564 frame_delay=default_frame_delay;
5568 simplicity=(size_t) mng_get_long(p);
5570 mng_type=1; /* Full MNG */
5572 if ((simplicity != 0) && ((simplicity | 11) == 11))
5573 mng_type=2; /* LC */
5575 if ((simplicity != 0) && ((simplicity | 9) == 9))
5576 mng_type=3; /* VLC */
5578 #if defined(MNG_INSERT_LAYERS)
5580 insert_layers=MagickTrue;
5582 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5584 /* Allocate next image structure. */
5585 AcquireNextImage(image_info,image,exception);
5587 if (GetNextImageInList(image) == (Image *) NULL)
5588 return((Image *) NULL);
5590 image=SyncNextImageInList(image);
5591 mng_info->image=image;
5594 if ((mng_info->mng_width > 65535L) ||
5595 (mng_info->mng_height > 65535L))
5597 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5598 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5601 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5602 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5603 mng_info->mng_height);
5605 mng_info->frame.left=0;
5606 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5607 mng_info->frame.top=0;
5608 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5609 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5611 for (i=0; i < MNG_MAX_OBJECTS; i++)
5612 mng_info->object_clip[i]=mng_info->frame;
5614 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5618 if (memcmp(type,mng_TERM,4) == 0)
5626 if (repeat == 3 && length > 9)
5628 final_delay=(png_uint_32) mng_get_long(&p[2]);
5629 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5631 if (mng_iterations == PNG_UINT_31_MAX)
5634 image->iterations=mng_iterations;
5635 term_chunk_found=MagickTrue;
5638 if (logging != MagickFalse)
5640 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5641 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5642 repeat,(double) final_delay, (double) image->iterations);
5645 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5648 if (memcmp(type,mng_DEFI,4) == 0)
5652 (void) ThrowMagickException(exception,GetMagickModule(),
5653 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5655 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5661 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5662 ThrowReaderException(CorruptImageError,"CorruptImage");
5665 object_id=((unsigned int) p[0] << 8) | (unsigned int) p[1];
5667 if (mng_type == 2 && object_id != 0)
5668 (void) ThrowMagickException(exception,GetMagickModule(),
5669 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5672 if (object_id >= MNG_MAX_OBJECTS)
5675 Instead of using a warning we should allocate a larger
5676 MngInfo structure and continue.
5678 (void) ThrowMagickException(exception,GetMagickModule(),
5679 CoderError,"object id too large","`%s'",image->filename);
5680 object_id=MNG_MAX_OBJECTS-1;
5683 if (mng_info->exists[object_id])
5684 if (mng_info->frozen[object_id])
5686 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5687 (void) ThrowMagickException(exception,
5688 GetMagickModule(),CoderError,
5689 "DEFI cannot redefine a frozen MNG object","`%s'",
5694 mng_info->exists[object_id]=MagickTrue;
5697 mng_info->invisible[object_id]=p[2];
5700 Extract object offset info.
5704 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5705 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5706 if (logging != MagickFalse)
5708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5709 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5710 object_id,(double) mng_info->x_off[object_id],
5711 object_id,(double) mng_info->y_off[object_id]);
5716 Extract object clipping info.
5719 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5722 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5725 if (memcmp(type,mng_bKGD,4) == 0)
5727 mng_info->have_global_bkgd=MagickFalse;
5731 mng_info->mng_global_bkgd.red=
5732 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5734 mng_info->mng_global_bkgd.green=
5735 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5737 mng_info->mng_global_bkgd.blue=
5738 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5740 mng_info->have_global_bkgd=MagickTrue;
5743 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5746 if (memcmp(type,mng_BACK,4) == 0)
5748 #if defined(MNG_INSERT_LAYERS)
5750 mandatory_back=p[6];
5755 if (mandatory_back && length > 5)
5757 mng_background_color.red=
5758 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5760 mng_background_color.green=
5761 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5763 mng_background_color.blue=
5764 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5766 mng_background_color.alpha=OpaqueAlpha;
5769 #ifdef MNG_OBJECT_BUFFERS
5771 mng_background_object=(p[7] << 8) | p[8];
5774 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5778 if (memcmp(type,mng_PLTE,4) == 0)
5780 /* Read global PLTE. */
5782 if (length && (length < 769))
5784 /* Read global PLTE. */
5786 if (mng_info->global_plte == (png_colorp) NULL)
5787 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5788 sizeof(*mng_info->global_plte));
5790 if (mng_info->global_plte == (png_colorp) NULL)
5792 mng_info->global_plte_length=0;
5793 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5794 ThrowReaderException(ResourceLimitError,
5795 "MemoryAllocationFailed");
5798 for (i=0; i < (ssize_t) (length/3); i++)
5800 mng_info->global_plte[i].red=p[3*i];
5801 mng_info->global_plte[i].green=p[3*i+1];
5802 mng_info->global_plte[i].blue=p[3*i+2];
5805 mng_info->global_plte_length=(unsigned int) (length/3);
5808 for ( ; i < 256; i++)
5810 mng_info->global_plte[i].red=i;
5811 mng_info->global_plte[i].green=i;
5812 mng_info->global_plte[i].blue=i;
5816 mng_info->global_plte_length=256;
5819 mng_info->global_plte_length=0;
5821 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5825 if (memcmp(type,mng_tRNS,4) == 0)
5827 /* read global tRNS */
5829 if (length > 0 && length < 257)
5830 for (i=0; i < (ssize_t) length; i++)
5831 mng_info->global_trns[i]=p[i];
5834 for ( ; i < 256; i++)
5835 mng_info->global_trns[i]=255;
5837 mng_info->global_trns_length=(unsigned int) length;
5838 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5841 if (memcmp(type,mng_gAMA,4) == 0)
5848 igamma=mng_get_long(p);
5849 mng_info->global_gamma=((float) igamma)*0.00001;
5850 mng_info->have_global_gama=MagickTrue;
5854 mng_info->have_global_gama=MagickFalse;
5856 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5860 if (memcmp(type,mng_cHRM,4) == 0)
5862 /* Read global cHRM */
5866 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5867 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5868 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5869 mng_info->global_chrm.red_primary.y=0.00001*
5870 mng_get_long(&p[12]);
5871 mng_info->global_chrm.green_primary.x=0.00001*
5872 mng_get_long(&p[16]);
5873 mng_info->global_chrm.green_primary.y=0.00001*
5874 mng_get_long(&p[20]);
5875 mng_info->global_chrm.blue_primary.x=0.00001*
5876 mng_get_long(&p[24]);
5877 mng_info->global_chrm.blue_primary.y=0.00001*
5878 mng_get_long(&p[28]);
5879 mng_info->have_global_chrm=MagickTrue;
5882 mng_info->have_global_chrm=MagickFalse;
5884 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5888 if (memcmp(type,mng_sRGB,4) == 0)
5895 mng_info->global_srgb_intent=
5896 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5897 mng_info->have_global_srgb=MagickTrue;
5900 mng_info->have_global_srgb=MagickFalse;
5902 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5906 if (memcmp(type,mng_iCCP,4) == 0)
5913 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5918 if (memcmp(type,mng_FRAM,4) == 0)
5921 (void) ThrowMagickException(exception,GetMagickModule(),
5922 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5925 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5926 image->delay=frame_delay;
5928 frame_delay=default_frame_delay;
5929 frame_timeout=default_frame_timeout;
5934 mng_info->framing_mode=p[0];
5936 if (logging != MagickFalse)
5937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5938 " Framing_mode=%d",mng_info->framing_mode);
5942 /* Note the delay and frame clipping boundaries. */
5944 p++; /* framing mode */
5946 while (((p-chunk) < (long) length) && *p)
5947 p++; /* frame name */
5949 p++; /* frame name terminator */
5951 if ((p-chunk) < (ssize_t) (length-4))
5958 change_delay=(*p++);
5959 change_timeout=(*p++);
5960 change_clipping=(*p++);
5961 p++; /* change_sync */
5963 if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5965 frame_delay=1UL*image->ticks_per_second*
5968 if (mng_info->ticks_per_second != 0)
5969 frame_delay/=mng_info->ticks_per_second;
5972 frame_delay=PNG_UINT_31_MAX;
5974 if (change_delay == 2)
5975 default_frame_delay=frame_delay;
5979 if (logging != MagickFalse)
5980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5981 " Framing_delay=%.20g",(double) frame_delay);
5984 if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
5986 frame_timeout=1UL*image->ticks_per_second*
5989 if (mng_info->ticks_per_second != 0)
5990 frame_timeout/=mng_info->ticks_per_second;
5993 frame_timeout=PNG_UINT_31_MAX;
5995 if (change_timeout == 2)
5996 default_frame_timeout=frame_timeout;
6000 if (logging != MagickFalse)
6001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6002 " Framing_timeout=%.20g",(double) frame_timeout);
6005 if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
6007 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
6011 if (logging != MagickFalse)
6012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6013 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
6014 (double) fb.left,(double) fb.right,(double) fb.top,
6015 (double) fb.bottom);
6017 if (change_clipping == 2)
6023 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6025 subframe_width=(size_t) (mng_info->clip.right
6026 -mng_info->clip.left);
6028 subframe_height=(size_t) (mng_info->clip.bottom
6029 -mng_info->clip.top);
6031 Insert a background layer behind the frame if framing_mode is 4.
6033 #if defined(MNG_INSERT_LAYERS)
6034 if (logging != MagickFalse)
6035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6036 " subframe_width=%.20g, subframe_height=%.20g",(double)
6037 subframe_width,(double) subframe_height);
6039 if (insert_layers && (mng_info->framing_mode == 4) &&
6040 (subframe_width) && (subframe_height))
6042 /* Allocate next image structure. */
6043 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6045 AcquireNextImage(image_info,image,exception);
6047 if (GetNextImageInList(image) == (Image *) NULL)
6048 return(DestroyImageList(image));
6050 image=SyncNextImageInList(image);
6053 mng_info->image=image;
6055 if (term_chunk_found)
6057 image->start_loop=MagickTrue;
6058 image->iterations=mng_iterations;
6059 term_chunk_found=MagickFalse;
6063 image->start_loop=MagickFalse;
6065 image->columns=subframe_width;
6066 image->rows=subframe_height;
6067 image->page.width=subframe_width;
6068 image->page.height=subframe_height;
6069 image->page.x=mng_info->clip.left;
6070 image->page.y=mng_info->clip.top;
6071 image->background_color=mng_background_color;
6072 image->alpha_trait=UndefinedPixelTrait;
6074 (void) SetImageBackgroundColor(image,exception);
6076 if (logging != MagickFalse)
6077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6078 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6079 (double) mng_info->clip.left,
6080 (double) mng_info->clip.right,
6081 (double) mng_info->clip.top,
6082 (double) mng_info->clip.bottom);
6085 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6089 if (memcmp(type,mng_CLIP,4) == 0)
6100 first_object=(p[0] << 8) | p[1];
6101 last_object=(p[2] << 8) | p[3];
6104 for (i=(int) first_object; i <= (int) last_object; i++)
6106 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6109 if (mng_info->exists[i] && !mng_info->frozen[i])
6114 box=mng_info->object_clip[i];
6115 if ((p-chunk) < (ssize_t) (length-17))
6116 mng_info->object_clip[i]=
6117 mng_read_box(box,(char) p[0],&p[1]);
6122 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6126 if (memcmp(type,mng_SAVE,4) == 0)
6128 for (i=1; i < MNG_MAX_OBJECTS; i++)
6129 if (mng_info->exists[i])
6131 mng_info->frozen[i]=MagickTrue;
6132 #ifdef MNG_OBJECT_BUFFERS
6133 if (mng_info->ob[i] != (MngBuffer *) NULL)
6134 mng_info->ob[i]->frozen=MagickTrue;
6138 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6143 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6145 /* Read DISC or SEEK. */
6147 if ((length == 0) || (length % 2) || !memcmp(type,mng_SEEK,4))
6149 for (i=1; i < MNG_MAX_OBJECTS; i++)
6150 MngInfoDiscardObject(mng_info,i);
6158 for (j=1; j < (ssize_t) length; j+=2)
6160 i=p[j-1] << 8 | p[j];
6161 MngInfoDiscardObject(mng_info,i);
6165 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6170 if (memcmp(type,mng_MOVE,4) == 0)
6180 first_object=(p[0] << 8) | p[1];
6181 last_object=(p[2] << 8) | p[3];
6184 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6186 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6189 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6190 (p-chunk) < (ssize_t) (length-8))
6198 old_pair.a=mng_info->x_off[i];
6199 old_pair.b=mng_info->y_off[i];
6200 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6201 mng_info->x_off[i]=new_pair.a;
6202 mng_info->y_off[i]=new_pair.b;
6207 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6211 if (memcmp(type,mng_LOOP,4) == 0)
6213 ssize_t loop_iters=1;
6216 loop_level=chunk[0];
6217 mng_info->loop_active[loop_level]=1; /* mark loop active */
6219 /* Record starting point. */
6220 loop_iters=mng_get_long(&chunk[1]);
6222 if (logging != MagickFalse)
6223 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6224 " LOOP level %.20g has %.20g iterations ",
6225 (double) loop_level, (double) loop_iters);
6227 if (loop_iters <= 0)
6228 skipping_loop=loop_level;
6232 if ((MagickSizeType) loop_iters > GetMagickResourceLimit(ListLengthResource))
6233 loop_iters=GetMagickResourceLimit(ListLengthResource);
6234 if (loop_iters >= 2147483647L)
6235 loop_iters=2147483647L;
6236 mng_info->loop_jump[loop_level]=TellBlob(image);
6237 mng_info->loop_count[loop_level]=loop_iters;
6240 mng_info->loop_iteration[loop_level]=0;
6242 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6246 if (memcmp(type,mng_ENDL,4) == 0)
6250 loop_level=chunk[0];
6252 if (skipping_loop > 0)
6254 if (skipping_loop == loop_level)
6257 Found end of zero-iteration loop.
6260 mng_info->loop_active[loop_level]=0;
6266 if (mng_info->loop_active[loop_level] == 1)
6268 mng_info->loop_count[loop_level]--;
6269 mng_info->loop_iteration[loop_level]++;
6271 if (logging != MagickFalse)
6272 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6273 " ENDL: LOOP level %.20g has %.20g remaining iters",
6274 (double) loop_level,(double)
6275 mng_info->loop_count[loop_level]);
6277 if (mng_info->loop_count[loop_level] != 0)
6280 SeekBlob(image,mng_info->loop_jump[loop_level],
6285 chunk=(unsigned char *) RelinquishMagickMemory(
6287 ThrowReaderException(CorruptImageError,
6288 "ImproperImageHeader");
6300 mng_info->loop_active[loop_level]=0;
6302 for (i=0; i < loop_level; i++)
6303 if (mng_info->loop_active[i] == 1)
6304 last_level=(short) i;
6305 loop_level=last_level;
6311 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6315 if (memcmp(type,mng_CLON,4) == 0)
6317 if (mng_info->clon_warning == 0)
6318 (void) ThrowMagickException(exception,GetMagickModule(),
6319 CoderError,"CLON is not implemented yet","`%s'",
6322 mng_info->clon_warning++;
6325 if (memcmp(type,mng_MAGN,4) == 0)
6340 magn_first=(p[0] << 8) | p[1];
6346 magn_last=(p[2] << 8) | p[3];
6349 magn_last=magn_first;
6350 #ifndef MNG_OBJECT_BUFFERS
6351 if (magn_first || magn_last)
6352 if (mng_info->magn_warning == 0)
6354 (void) ThrowMagickException(exception,
6355 GetMagickModule(),CoderError,
6356 "MAGN is not implemented yet for nonzero objects",
6357 "`%s'",image->filename);
6359 mng_info->magn_warning++;
6369 magn_mx=(p[5] << 8) | p[6];
6378 magn_my=(p[7] << 8) | p[8];
6387 magn_ml=(p[9] << 8) | p[10];
6396 magn_mr=(p[11] << 8) | p[12];
6405 magn_mt=(p[13] << 8) | p[14];
6414 magn_mb=(p[15] << 8) | p[16];
6426 magn_methy=magn_methx;
6429 if (magn_methx > 5 || magn_methy > 5)
6430 if (mng_info->magn_warning == 0)
6432 (void) ThrowMagickException(exception,
6433 GetMagickModule(),CoderError,
6434 "Unknown MAGN method in MNG datastream","`%s'",
6437 mng_info->magn_warning++;
6439 #ifdef MNG_OBJECT_BUFFERS
6440 /* Magnify existing objects in the range magn_first to magn_last */
6442 if (magn_first == 0 || magn_last == 0)
6444 /* Save the magnification factors for object 0 */
6445 mng_info->magn_mb=magn_mb;
6446 mng_info->magn_ml=magn_ml;
6447 mng_info->magn_mr=magn_mr;
6448 mng_info->magn_mt=magn_mt;
6449 mng_info->magn_mx=magn_mx;
6450 mng_info->magn_my=magn_my;
6451 mng_info->magn_methx=magn_methx;
6452 mng_info->magn_methy=magn_methy;
6456 if (memcmp(type,mng_PAST,4) == 0)
6458 if (mng_info->past_warning == 0)
6459 (void) ThrowMagickException(exception,GetMagickModule(),
6460 CoderError,"PAST is not implemented yet","`%s'",
6463 mng_info->past_warning++;
6466 if (memcmp(type,mng_SHOW,4) == 0)
6468 if (mng_info->show_warning == 0)
6469 (void) ThrowMagickException(exception,GetMagickModule(),
6470 CoderError,"SHOW is not implemented yet","`%s'",
6473 mng_info->show_warning++;
6476 if (memcmp(type,mng_sBIT,4) == 0)
6479 mng_info->have_global_sbit=MagickFalse;
6483 mng_info->global_sbit.gray=p[0];
6484 mng_info->global_sbit.red=p[0];
6485 mng_info->global_sbit.green=p[1];
6486 mng_info->global_sbit.blue=p[2];
6487 mng_info->global_sbit.alpha=p[3];
6488 mng_info->have_global_sbit=MagickTrue;
6491 if (memcmp(type,mng_pHYs,4) == 0)
6495 mng_info->global_x_pixels_per_unit=
6496 (size_t) mng_get_long(p);
6497 mng_info->global_y_pixels_per_unit=
6498 (size_t) mng_get_long(&p[4]);
6499 mng_info->global_phys_unit_type=p[8];
6500 mng_info->have_global_phys=MagickTrue;
6504 mng_info->have_global_phys=MagickFalse;
6506 if (memcmp(type,mng_pHYg,4) == 0)
6508 if (mng_info->phyg_warning == 0)
6509 (void) ThrowMagickException(exception,GetMagickModule(),
6510 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6512 mng_info->phyg_warning++;
6514 if (memcmp(type,mng_BASI,4) == 0)
6516 skip_to_iend=MagickTrue;
6518 if (mng_info->basi_warning == 0)
6519 (void) ThrowMagickException(exception,GetMagickModule(),
6520 CoderError,"BASI is not implemented yet","`%s'",
6523 mng_info->basi_warning++;
6524 #ifdef MNG_BASI_SUPPORTED
6525 basi_width=(unsigned long) mng_get_long(p);
6526 basi_width=(unsigned long) mng_get_long(&p[4]);
6527 basi_color_type=p[8];
6528 basi_compression_method=p[9];
6529 basi_filter_type=p[10];
6530 basi_interlace_method=p[11];
6532 basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6538 basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6544 basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6550 basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6554 if (basi_sample_depth == 16)
6561 basi_viewable=p[20];
6567 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6571 if (memcmp(type,mng_IHDR,4)
6572 #if defined(JNG_SUPPORTED)
6573 && memcmp(type,mng_JHDR,4)
6577 /* Not an IHDR or JHDR chunk */
6578 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6583 if (logging != MagickFalse)
6584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6585 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6587 mng_info->exists[object_id]=MagickTrue;
6588 mng_info->viewable[object_id]=MagickTrue;
6590 if (mng_info->invisible[object_id])
6592 if (logging != MagickFalse)
6593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6594 " Skipping invisible object");
6596 skip_to_iend=MagickTrue;
6597 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6600 #if defined(MNG_INSERT_LAYERS)
6603 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6604 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6607 image_width=(size_t) mng_get_long(p);
6608 image_height=(size_t) mng_get_long(&p[4]);
6610 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6613 Insert a transparent background layer behind the entire animation
6614 if it is not full screen.
6616 #if defined(MNG_INSERT_LAYERS)
6617 if (insert_layers && mng_type && first_mng_object)
6619 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6620 (image_width < mng_info->mng_width) ||
6621 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6622 (image_height < mng_info->mng_height) ||
6623 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6625 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6628 Allocate next image structure.
6630 AcquireNextImage(image_info,image,exception);
6632 if (GetNextImageInList(image) == (Image *) NULL)
6633 return(DestroyImageList(image));
6635 image=SyncNextImageInList(image);
6637 mng_info->image=image;
6639 if (term_chunk_found)
6641 image->start_loop=MagickTrue;
6642 image->iterations=mng_iterations;
6643 term_chunk_found=MagickFalse;
6647 image->start_loop=MagickFalse;
6649 /* Make a background rectangle. */
6652 image->columns=mng_info->mng_width;
6653 image->rows=mng_info->mng_height;
6654 image->page.width=mng_info->mng_width;
6655 image->page.height=mng_info->mng_height;
6658 image->background_color=mng_background_color;
6659 (void) SetImageBackgroundColor(image,exception);
6660 if (logging != MagickFalse)
6661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6662 " Inserted transparent background layer, W=%.20g, H=%.20g",
6663 (double) mng_info->mng_width,(double) mng_info->mng_height);
6667 Insert a background layer behind the upcoming image if
6668 framing_mode is 3, and we haven't already inserted one.
6670 if (insert_layers && (mng_info->framing_mode == 3) &&
6671 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6672 (simplicity & 0x08)))
6674 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6677 Allocate next image structure.
6679 AcquireNextImage(image_info,image,exception);
6681 if (GetNextImageInList(image) == (Image *) NULL)
6682 return(DestroyImageList(image));
6684 image=SyncNextImageInList(image);
6687 mng_info->image=image;
6689 if (term_chunk_found)
6691 image->start_loop=MagickTrue;
6692 image->iterations=mng_iterations;
6693 term_chunk_found=MagickFalse;
6697 image->start_loop=MagickFalse;
6700 image->columns=subframe_width;
6701 image->rows=subframe_height;
6702 image->page.width=subframe_width;
6703 image->page.height=subframe_height;
6704 image->page.x=mng_info->clip.left;
6705 image->page.y=mng_info->clip.top;
6706 image->background_color=mng_background_color;
6707 image->alpha_trait=UndefinedPixelTrait;
6708 (void) SetImageBackgroundColor(image,exception);
6710 if (logging != MagickFalse)
6711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6712 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6713 (double) mng_info->clip.left,(double) mng_info->clip.right,
6714 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6716 #endif /* MNG_INSERT_LAYERS */
6717 first_mng_object=MagickFalse;
6719 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6722 Allocate next image structure.
6724 AcquireNextImage(image_info,image,exception);
6726 if (GetNextImageInList(image) == (Image *) NULL)
6727 return(DestroyImageList(image));
6729 image=SyncNextImageInList(image);
6731 mng_info->image=image;
6732 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6733 GetBlobSize(image));
6735 if (status == MagickFalse)
6738 if (term_chunk_found)
6740 image->start_loop=MagickTrue;
6741 term_chunk_found=MagickFalse;
6745 image->start_loop=MagickFalse;
6747 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6749 image->delay=frame_delay;
6750 frame_delay=default_frame_delay;
6756 image->page.width=mng_info->mng_width;
6757 image->page.height=mng_info->mng_height;
6758 image->page.x=mng_info->x_off[object_id];
6759 image->page.y=mng_info->y_off[object_id];
6760 image->iterations=mng_iterations;
6763 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6766 if (logging != MagickFalse)
6767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6768 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6771 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6774 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6777 mng_info->image=image;
6778 mng_info->mng_type=mng_type;
6779 mng_info->object_id=object_id;
6781 if (memcmp(type,mng_IHDR,4) == 0)
6782 image=ReadOnePNGImage(mng_info,image_info,exception);
6784 #if defined(JNG_SUPPORTED)
6786 image=ReadOneJNGImage(mng_info,image_info,exception);
6789 if (image == (Image *) NULL)
6791 if (logging != MagickFalse)
6792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6793 "exit ReadJNGImage() with error");
6795 return((Image *) NULL);
6798 if (image->columns == 0 || image->rows == 0)
6800 (void) CloseBlob(image);
6801 return(DestroyImageList(image));
6804 mng_info->image=image;
6811 if (mng_info->magn_methx || mng_info->magn_methy)
6817 if (logging != MagickFalse)
6818 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6819 " Processing MNG MAGN chunk");
6821 if (mng_info->magn_methx == 1)
6823 magnified_width=mng_info->magn_ml;
6825 if (image->columns > 1)
6826 magnified_width += mng_info->magn_mr;
6828 if (image->columns > 2)
6829 magnified_width += (png_uint_32)
6830 ((image->columns-2)*(mng_info->magn_mx));
6835 magnified_width=(png_uint_32) image->columns;
6837 if (image->columns > 1)
6838 magnified_width += mng_info->magn_ml-1;
6840 if (image->columns > 2)
6841 magnified_width += mng_info->magn_mr-1;
6843 if (image->columns > 3)
6844 magnified_width += (png_uint_32)
6845 ((image->columns-3)*(mng_info->magn_mx-1));
6848 if (mng_info->magn_methy == 1)
6850 magnified_height=mng_info->magn_mt;
6852 if (image->rows > 1)
6853 magnified_height += mng_info->magn_mb;
6855 if (image->rows > 2)
6856 magnified_height += (png_uint_32)
6857 ((image->rows-2)*(mng_info->magn_my));
6862 magnified_height=(png_uint_32) image->rows;
6864 if (image->rows > 1)
6865 magnified_height += mng_info->magn_mt-1;
6867 if (image->rows > 2)
6868 magnified_height += mng_info->magn_mb-1;
6870 if (image->rows > 3)
6871 magnified_height += (png_uint_32)
6872 ((image->rows-3)*(mng_info->magn_my-1));
6875 if (magnified_height > image->rows ||
6876 magnified_width > image->columns)
6903 /* Allocate next image structure. */
6905 if (logging != MagickFalse)
6906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6907 " Allocate magnified image");
6909 AcquireNextImage(image_info,image,exception);
6911 if (GetNextImageInList(image) == (Image *) NULL)
6912 return(DestroyImageList(image));
6914 large_image=SyncNextImageInList(image);
6916 large_image->columns=magnified_width;
6917 large_image->rows=magnified_height;
6919 magn_methx=mng_info->magn_methx;
6920 magn_methy=mng_info->magn_methy;
6922 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6923 #define QM unsigned short
6924 if (magn_methx != 1 || magn_methy != 1)
6927 Scale pixels to unsigned shorts to prevent
6928 overflow of intermediate values of interpolations
6930 for (y=0; y < (ssize_t) image->rows; y++)
6932 q=GetAuthenticPixels(image,0,y,image->columns,1,
6934 if (q == (Quantum *) NULL)
6936 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6938 SetPixelRed(image,ScaleQuantumToShort(
6939 GetPixelRed(image,q)),q);
6940 SetPixelGreen(image,ScaleQuantumToShort(
6941 GetPixelGreen(image,q)),q);
6942 SetPixelBlue(image,ScaleQuantumToShort(
6943 GetPixelBlue(image,q)),q);
6944 SetPixelAlpha(image,ScaleQuantumToShort(
6945 GetPixelAlpha(image,q)),q);
6946 q+=GetPixelChannels(image);
6949 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6957 if (image->alpha_trait != UndefinedPixelTrait)
6958 (void) SetImageBackgroundColor(large_image,exception);
6962 large_image->background_color.alpha=OpaqueAlpha;
6963 (void) SetImageBackgroundColor(large_image,exception);
6965 if (magn_methx == 4)
6968 if (magn_methx == 5)
6971 if (magn_methy == 4)
6974 if (magn_methy == 5)
6978 /* magnify the rows into the right side of the large image */
6980 if (logging != MagickFalse)
6981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6982 " Magnify the rows to %.20g",
6983 (double) large_image->rows);
6984 m=(ssize_t) mng_info->magn_mt;
6986 length=(size_t) GetPixelChannels(image)*image->columns;
6987 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6988 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6990 if ((prev == (Quantum *) NULL) ||
6991 (next == (Quantum *) NULL))
6993 if (prev != (Quantum *) NULL)
6994 prev=(Quantum *) RelinquishMagickMemory(prev);
6995 if (next != (Quantum *) NULL)
6996 next=(Quantum *) RelinquishMagickMemory(next);
6997 image=DestroyImageList(image);
6998 ThrowReaderException(ResourceLimitError,
6999 "MemoryAllocationFailed");
7002 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
7003 (void) memcpy(next,n,length);
7005 for (y=0; y < (ssize_t) image->rows; y++)
7008 m=(ssize_t) mng_info->magn_mt;
7010 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
7011 m=(ssize_t) mng_info->magn_mb;
7013 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
7014 m=(ssize_t) mng_info->magn_mb;
7016 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
7020 m=(ssize_t) mng_info->magn_my;
7026 if (y < (ssize_t) image->rows-1)
7028 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
7030 (void) memcpy(next,n,length);
7033 for (i=0; i < m; i++, yy++)
7038 assert(yy < (ssize_t) large_image->rows);
7041 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7043 if (q == (Quantum *) NULL)
7045 q+=(large_image->columns-image->columns)*
7046 GetPixelChannels(large_image);
7048 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7050 /* To do: get color as function of indexes[x] */
7052 if (image->storage_class == PseudoClass)
7057 if (magn_methy <= 1)
7059 /* replicate previous */
7060 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7061 SetPixelGreen(large_image,GetPixelGreen(image,
7063 SetPixelBlue(large_image,GetPixelBlue(image,
7065 SetPixelAlpha(large_image,GetPixelAlpha(image,
7069 else if (magn_methy == 2 || magn_methy == 4)
7073 SetPixelRed(large_image,GetPixelRed(image,
7075 SetPixelGreen(large_image,GetPixelGreen(image,
7077 SetPixelBlue(large_image,GetPixelBlue(image,
7079 SetPixelAlpha(large_image,GetPixelAlpha(image,
7086 SetPixelRed(large_image,((QM) (((ssize_t)
7087 (2*i*(GetPixelRed(image,n)
7088 -GetPixelRed(image,pixels)+m))/
7090 +GetPixelRed(image,pixels)))),q);
7091 SetPixelGreen(large_image,((QM) (((ssize_t)
7092 (2*i*(GetPixelGreen(image,n)
7093 -GetPixelGreen(image,pixels)+m))/
7095 +GetPixelGreen(image,pixels)))),q);
7096 SetPixelBlue(large_image,((QM) (((ssize_t)
7097 (2*i*(GetPixelBlue(image,n)
7098 -GetPixelBlue(image,pixels)+m))/
7100 +GetPixelBlue(image,pixels)))),q);
7102 if (image->alpha_trait != UndefinedPixelTrait)
7103 SetPixelAlpha(large_image, ((QM) (((ssize_t)
7104 (2*i*(GetPixelAlpha(image,n)
7105 -GetPixelAlpha(image,pixels)+m))
7107 GetPixelAlpha(image,pixels)))),q);
7110 if (magn_methy == 4)
7112 /* Replicate nearest */
7113 if (i <= ((m+1) << 1))
7114 SetPixelAlpha(large_image,GetPixelAlpha(image,
7117 SetPixelAlpha(large_image,GetPixelAlpha(image,
7122 else /* if (magn_methy == 3 || magn_methy == 5) */
7124 /* Replicate nearest */
7125 if (i <= ((m+1) << 1))
7127 SetPixelRed(large_image,GetPixelRed(image,
7129 SetPixelGreen(large_image,GetPixelGreen(image,
7131 SetPixelBlue(large_image,GetPixelBlue(image,
7133 SetPixelAlpha(large_image,GetPixelAlpha(image,
7139 SetPixelRed(large_image,GetPixelRed(image,n),q);
7140 SetPixelGreen(large_image,GetPixelGreen(image,n),
7142 SetPixelBlue(large_image,GetPixelBlue(image,n),
7144 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7148 if (magn_methy == 5)
7150 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7151 (GetPixelAlpha(image,n)
7152 -GetPixelAlpha(image,pixels))
7153 +m))/((ssize_t) (m*2))
7154 +GetPixelAlpha(image,pixels)),q);
7157 n+=GetPixelChannels(image);
7158 q+=GetPixelChannels(large_image);
7159 pixels+=GetPixelChannels(image);
7162 if (SyncAuthenticPixels(large_image,exception) == 0)
7168 prev=(Quantum *) RelinquishMagickMemory(prev);
7169 next=(Quantum *) RelinquishMagickMemory(next);
7171 length=image->columns;
7173 if (logging != MagickFalse)
7174 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7175 " Delete original image");
7177 DeleteImageFromList(&image);
7181 mng_info->image=image;
7183 /* magnify the columns */
7184 if (logging != MagickFalse)
7185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7186 " Magnify the columns to %.20g",
7187 (double) image->columns);
7189 for (y=0; y < (ssize_t) image->rows; y++)
7194 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7195 if (q == (Quantum *) NULL)
7197 pixels=q+(image->columns-length)*GetPixelChannels(image);
7198 n=pixels+GetPixelChannels(image);
7200 for (x=(ssize_t) (image->columns-length);
7201 x < (ssize_t) image->columns; x++)
7203 /* To do: Rewrite using Get/Set***PixelChannel() */
7205 if (x == (ssize_t) (image->columns-length))
7206 m=(ssize_t) mng_info->magn_ml;
7208 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7209 m=(ssize_t) mng_info->magn_mr;
7211 else if (magn_methx <= 1 &&
7212 x == (ssize_t) image->columns-1)
7213 m=(ssize_t) mng_info->magn_mr;
7215 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7219 m=(ssize_t) mng_info->magn_mx;
7221 for (i=0; i < m; i++)
7223 if (magn_methx <= 1)
7225 /* replicate previous */
7226 SetPixelRed(image,GetPixelRed(image,pixels),q);
7227 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7228 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7229 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7232 else if (magn_methx == 2 || magn_methx == 4)
7236 SetPixelRed(image,GetPixelRed(image,pixels),q);
7237 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7238 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7239 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7242 /* To do: Rewrite using Get/Set***PixelChannel() */
7246 SetPixelRed(image,(QM) ((2*i*(
7247 GetPixelRed(image,n)
7248 -GetPixelRed(image,pixels))+m)
7250 GetPixelRed(image,pixels)),q);
7252 SetPixelGreen(image,(QM) ((2*i*(
7253 GetPixelGreen(image,n)
7254 -GetPixelGreen(image,pixels))+m)
7256 GetPixelGreen(image,pixels)),q);
7258 SetPixelBlue(image,(QM) ((2*i*(
7259 GetPixelBlue(image,n)
7260 -GetPixelBlue(image,pixels))+m)
7262 GetPixelBlue(image,pixels)),q);
7263 if (image->alpha_trait != UndefinedPixelTrait)
7264 SetPixelAlpha(image,(QM) ((2*i*(
7265 GetPixelAlpha(image,n)
7266 -GetPixelAlpha(image,pixels))+m)
7268 GetPixelAlpha(image,pixels)),q);
7271 if (magn_methx == 4)
7273 /* Replicate nearest */
7274 if (i <= ((m+1) << 1))
7276 SetPixelAlpha(image,
7277 GetPixelAlpha(image,pixels)+0,q);
7281 SetPixelAlpha(image,
7282 GetPixelAlpha(image,n)+0,q);
7287 else /* if (magn_methx == 3 || magn_methx == 5) */
7289 /* Replicate nearest */
7290 if (i <= ((m+1) << 1))
7292 SetPixelRed(image,GetPixelRed(image,pixels),q);
7293 SetPixelGreen(image,GetPixelGreen(image,
7295 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7296 SetPixelAlpha(image,GetPixelAlpha(image,
7302 SetPixelRed(image,GetPixelRed(image,n),q);
7303 SetPixelGreen(image,GetPixelGreen(image,n),q);
7304 SetPixelBlue(image,GetPixelBlue(image,n),q);
7305 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7308 if (magn_methx == 5)
7311 SetPixelAlpha(image,
7312 (QM) ((2*i*( GetPixelAlpha(image,n)
7313 -GetPixelAlpha(image,pixels))+m)/
7315 +GetPixelAlpha(image,pixels)),q);
7318 q+=GetPixelChannels(image);
7320 n+=GetPixelChannels(image);
7323 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7326 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7327 if (magn_methx != 1 || magn_methy != 1)
7330 Rescale pixels to Quantum
7332 for (y=0; y < (ssize_t) image->rows; y++)
7334 q=GetAuthenticPixels(image,0,y,image->columns,1,
7336 if (q == (Quantum *) NULL)
7339 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7341 SetPixelRed(image,ScaleShortToQuantum(
7342 GetPixelRed(image,q)),q);
7343 SetPixelGreen(image,ScaleShortToQuantum(
7344 GetPixelGreen(image,q)),q);
7345 SetPixelBlue(image,ScaleShortToQuantum(
7346 GetPixelBlue(image,q)),q);
7347 SetPixelAlpha(image,ScaleShortToQuantum(
7348 GetPixelAlpha(image,q)),q);
7349 q+=GetPixelChannels(image);
7352 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7357 if (logging != MagickFalse)
7358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7359 " Finished MAGN processing");
7364 Crop_box is with respect to the upper left corner of the MNG.
7366 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7367 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7368 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7369 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7370 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7371 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7372 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7373 if ((crop_box.left != (mng_info->image_box.left
7374 +mng_info->x_off[object_id])) ||
7375 (crop_box.right != (mng_info->image_box.right
7376 +mng_info->x_off[object_id])) ||
7377 (crop_box.top != (mng_info->image_box.top
7378 +mng_info->y_off[object_id])) ||
7379 (crop_box.bottom != (mng_info->image_box.bottom
7380 +mng_info->y_off[object_id])))
7382 if (logging != MagickFalse)
7383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7384 " Crop the PNG image");
7386 if ((crop_box.left < crop_box.right) &&
7387 (crop_box.top < crop_box.bottom))
7396 Crop_info is with respect to the upper left corner of
7399 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7400 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7401 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7402 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7403 image->page.width=image->columns;
7404 image->page.height=image->rows;
7407 im=CropImage(image,&crop_info,exception);
7409 if (im != (Image *) NULL)
7411 image->columns=im->columns;
7412 image->rows=im->rows;
7413 im=DestroyImage(im);
7414 image->page.width=image->columns;
7415 image->page.height=image->rows;
7416 image->page.x=crop_box.left;
7417 image->page.y=crop_box.top;
7424 No pixels in crop area. The MNG spec still requires
7425 a layer, though, so make a single transparent pixel in
7426 the top left corner.
7431 (void) SetImageBackgroundColor(image,exception);
7432 image->page.width=1;
7433 image->page.height=1;
7438 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7439 image=mng_info->image;
7443 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7444 /* PNG does not handle depths greater than 16 so reduce it even
7447 if (image->depth > 16)
7451 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7452 if (image->depth > 8)
7454 /* To do: fill low byte properly */
7458 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7462 if (image_info->number_scenes != 0)
7464 if (mng_info->scenes_found >
7465 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7469 if (logging != MagickFalse)
7470 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7471 " Finished reading image datastream.");
7473 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7475 (void) CloseBlob(image);
7477 if (logging != MagickFalse)
7478 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7479 " Finished reading all image datastreams.");
7481 #if defined(MNG_INSERT_LAYERS)
7482 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7483 (mng_info->mng_height))
7486 Insert a background layer if nothing else was found.
7488 if (logging != MagickFalse)
7489 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7490 " No images found. Inserting a background layer.");
7492 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7495 Allocate next image structure.
7497 AcquireNextImage(image_info,image,exception);
7498 if (GetNextImageInList(image) == (Image *) NULL)
7500 if (logging != MagickFalse)
7501 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7502 " Allocation failed, returning NULL.");
7504 return(DestroyImageList(image));;
7506 image=SyncNextImageInList(image);
7508 image->columns=mng_info->mng_width;
7509 image->rows=mng_info->mng_height;
7510 image->page.width=mng_info->mng_width;
7511 image->page.height=mng_info->mng_height;
7514 image->background_color=mng_background_color;
7515 image->alpha_trait=UndefinedPixelTrait;
7517 if (image_info->ping == MagickFalse)
7518 (void) SetImageBackgroundColor(image,exception);
7520 mng_info->image_found++;
7523 image->iterations=mng_iterations;
7525 if (mng_iterations == 1)
7526 image->start_loop=MagickTrue;
7528 while (GetPreviousImageInList(image) != (Image *) NULL)
7531 if (image_count > 10*mng_info->image_found)
7533 if (logging != MagickFalse)
7534 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7536 (void) ThrowMagickException(exception,GetMagickModule(),
7537 CoderError,"Linked list is corrupted, beginning of list not found",
7538 "`%s'",image_info->filename);
7540 return(DestroyImageList(image));
7543 image=GetPreviousImageInList(image);
7545 if (GetNextImageInList(image) == (Image *) NULL)
7547 if (logging != MagickFalse)
7548 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7550 (void) ThrowMagickException(exception,GetMagickModule(),
7551 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7552 image_info->filename);
7556 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7557 GetNextImageInList(image) ==
7560 if (logging != MagickFalse)
7561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7562 " First image null");
7564 (void) ThrowMagickException(exception,GetMagickModule(),
7565 CoderError,"image->next for first image is NULL but shouldn't be.",
7566 "`%s'",image_info->filename);
7569 if (mng_info->image_found == 0)
7571 if (logging != MagickFalse)
7572 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7573 " No visible images found.");
7575 (void) ThrowMagickException(exception,GetMagickModule(),
7576 CoderError,"No visible images in file","`%s'",image_info->filename);
7578 return(DestroyImageList(image));
7581 if (mng_info->ticks_per_second)
7582 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7583 final_delay/mng_info->ticks_per_second;
7586 image->start_loop=MagickTrue;
7588 /* Find final nonzero image delay */
7589 final_image_delay=0;
7591 while (GetNextImageInList(image) != (Image *) NULL)
7594 final_image_delay=image->delay;
7596 image=GetNextImageInList(image);
7599 if (final_delay < final_image_delay)
7600 final_delay=final_image_delay;
7602 image->delay=final_delay;
7604 if (logging != MagickFalse)
7605 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7606 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7607 (double) final_delay);
7609 if (logging != MagickFalse)
7615 image=GetFirstImageInList(image);
7617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7618 " Before coalesce:");
7620 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7621 " scene 0 delay=%.20g",(double) image->delay);
7623 while (GetNextImageInList(image) != (Image *) NULL)
7625 image=GetNextImageInList(image);
7626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7627 " scene %.20g delay=%.20g",(double) scene++,
7628 (double) image->delay);
7632 image=GetFirstImageInList(image);
7633 #ifdef MNG_COALESCE_LAYERS
7643 if (logging != MagickFalse)
7644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7645 " Coalesce Images");
7648 next_image=CoalesceImages(image,exception);
7650 if (next_image == (Image *) NULL)
7651 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7653 image=DestroyImageList(image);
7656 for (next=image; next != (Image *) NULL; next=next_image)
7658 next->page.width=mng_info->mng_width;
7659 next->page.height=mng_info->mng_height;
7662 next->scene=scene++;
7663 next_image=GetNextImageInList(next);
7665 if (next_image == (Image *) NULL)
7668 if (next->delay == 0)
7671 next_image->previous=GetPreviousImageInList(next);
7672 if (GetPreviousImageInList(next) == (Image *) NULL)
7675 next->previous->next=next_image;
7676 next=DestroyImage(next);
7682 while (GetNextImageInList(image) != (Image *) NULL)
7683 image=GetNextImageInList(image);
7685 image->dispose=BackgroundDispose;
7687 if (logging != MagickFalse)
7693 image=GetFirstImageInList(image);
7695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7696 " After coalesce:");
7698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7699 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7700 (double) image->dispose);
7702 while (GetNextImageInList(image) != (Image *) NULL)
7704 image=GetNextImageInList(image);
7706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7707 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7708 (double) image->delay,(double) image->dispose);
7712 if (logging != MagickFalse)
7713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7714 " exit ReadOneMNGImage();");
7719 static Image *ReadMNGImage(const ImageInfo *image_info,
7720 ExceptionInfo *exception)
7732 /* Open image file. */
7734 assert(image_info != (const ImageInfo *) NULL);
7735 assert(image_info->signature == MagickCoreSignature);
7736 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7737 image_info->filename);
7738 assert(exception != (ExceptionInfo *) NULL);
7739 assert(exception->signature == MagickCoreSignature);
7740 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7741 image=AcquireImage(image_info,exception);
7742 mng_info=(MngInfo *) NULL;
7743 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7745 if (status == MagickFalse)
7746 return(DestroyImageList(image));
7748 /* Allocate a MngInfo structure. */
7750 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7752 if (mng_info == (MngInfo *) NULL)
7753 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7755 /* Initialize members of the MngInfo structure. */
7757 (void) memset(mng_info,0,sizeof(MngInfo));
7758 mng_info->image=image;
7759 image=ReadOneMNGImage(mng_info,image_info,exception);
7760 mng_info=MngInfoFreeStruct(mng_info);
7762 if (image == (Image *) NULL)
7764 if (logging != MagickFalse)
7765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7766 "exit ReadMNGImage() with error");
7768 return((Image *) NULL);
7770 (void) CloseBlob(image);
7772 if (logging != MagickFalse)
7773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7775 return(GetFirstImageInList(image));
7777 #else /* PNG_LIBPNG_VER > 10011 */
7778 static Image *ReadPNGImage(const ImageInfo *image_info,
7779 ExceptionInfo *exception)
7781 printf("Your PNG library is too old: You have libpng-%s\n",
7782 PNG_LIBPNG_VER_STRING);
7784 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7785 "PNG library is too old","`%s'",image_info->filename);
7787 return(Image *) NULL;
7790 static Image *ReadMNGImage(const ImageInfo *image_info,
7791 ExceptionInfo *exception)
7793 return(ReadPNGImage(image_info,exception));
7795 #endif /* PNG_LIBPNG_VER > 10011 */
7799 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7803 % R e g i s t e r P N G I m a g e %
7807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7809 % RegisterPNGImage() adds properties for the PNG image format to
7810 % the list of supported formats. The properties include the image format
7811 % tag, a method to read and/or write the format, whether the format
7812 % supports the saving of more than one frame to the same file or blob,
7813 % whether the format supports native in-memory I/O, and a brief
7814 % description of the format.
7816 % The format of the RegisterPNGImage method is:
7818 % size_t RegisterPNGImage(void)
7821 ModuleExport size_t RegisterPNGImage(void)
7824 version[MagickPathExtent];
7832 "See http://www.libpng.org/ for details about the PNG format."
7837 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7843 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7849 #if defined(PNG_LIBPNG_VER_STRING)
7850 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7851 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7854 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7856 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7857 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7862 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7863 entry->flags|=CoderDecoderSeekableStreamFlag;
7865 #if defined(MAGICKCORE_PNG_DELEGATE)
7866 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7867 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7870 entry->magick=(IsImageFormatHandler *) IsMNG;
7872 if (*version != '\0')
7873 entry->version=ConstantString(version);
7875 entry->mime_type=ConstantString("video/x-mng");
7876 entry->note=ConstantString(MNGNote);
7877 (void) RegisterMagickInfo(entry);
7879 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7881 #if defined(MAGICKCORE_PNG_DELEGATE)
7882 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7883 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7886 entry->magick=(IsImageFormatHandler *) IsPNG;
7887 entry->flags|=CoderDecoderSeekableStreamFlag;
7888 entry->flags^=CoderAdjoinFlag;
7889 entry->mime_type=ConstantString("image/png");
7891 if (*version != '\0')
7892 entry->version=ConstantString(version);
7894 entry->note=ConstantString(PNGNote);
7895 (void) RegisterMagickInfo(entry);
7897 entry=AcquireMagickInfo("PNG","PNG8",
7898 "8-bit indexed with optional binary transparency");
7900 #if defined(MAGICKCORE_PNG_DELEGATE)
7901 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7902 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7905 entry->magick=(IsImageFormatHandler *) IsPNG;
7906 entry->flags|=CoderDecoderSeekableStreamFlag;
7907 entry->flags^=CoderAdjoinFlag;
7908 entry->mime_type=ConstantString("image/png");
7909 (void) RegisterMagickInfo(entry);
7911 entry=AcquireMagickInfo("PNG","PNG24",
7912 "opaque or binary transparent 24-bit RGB");
7915 #if defined(ZLIB_VERSION)
7916 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7917 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7919 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7921 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7922 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7926 if (*version != '\0')
7927 entry->version=ConstantString(version);
7929 #if defined(MAGICKCORE_PNG_DELEGATE)
7930 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7931 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7934 entry->magick=(IsImageFormatHandler *) IsPNG;
7935 entry->flags|=CoderDecoderSeekableStreamFlag;
7936 entry->flags^=CoderAdjoinFlag;
7937 entry->mime_type=ConstantString("image/png");
7938 (void) RegisterMagickInfo(entry);
7940 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7942 #if defined(MAGICKCORE_PNG_DELEGATE)
7943 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7944 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7947 entry->magick=(IsImageFormatHandler *) IsPNG;
7948 entry->flags|=CoderDecoderSeekableStreamFlag;
7949 entry->flags^=CoderAdjoinFlag;
7950 entry->mime_type=ConstantString("image/png");
7951 (void) RegisterMagickInfo(entry);
7953 entry=AcquireMagickInfo("PNG","PNG48",
7954 "opaque or binary transparent 48-bit RGB");
7956 #if defined(MAGICKCORE_PNG_DELEGATE)
7957 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7958 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7961 entry->magick=(IsImageFormatHandler *) IsPNG;
7962 entry->flags|=CoderDecoderSeekableStreamFlag;
7963 entry->flags^=CoderAdjoinFlag;
7964 entry->mime_type=ConstantString("image/png");
7965 (void) RegisterMagickInfo(entry);
7967 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7969 #if defined(MAGICKCORE_PNG_DELEGATE)
7970 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7971 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7974 entry->magick=(IsImageFormatHandler *) IsPNG;
7975 entry->flags|=CoderDecoderSeekableStreamFlag;
7976 entry->flags^=CoderAdjoinFlag;
7977 entry->mime_type=ConstantString("image/png");
7978 (void) RegisterMagickInfo(entry);
7980 entry=AcquireMagickInfo("PNG","PNG00",
7981 "PNG inheriting bit-depth, color-type from original, if possible");
7983 #if defined(MAGICKCORE_PNG_DELEGATE)
7984 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7985 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7988 entry->magick=(IsImageFormatHandler *) IsPNG;
7989 entry->flags|=CoderDecoderSeekableStreamFlag;
7990 entry->flags^=CoderAdjoinFlag;
7991 entry->mime_type=ConstantString("image/png");
7992 (void) RegisterMagickInfo(entry);
7994 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7996 #if defined(JNG_SUPPORTED)
7997 #if defined(MAGICKCORE_PNG_DELEGATE)
7998 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7999 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
8003 entry->magick=(IsImageFormatHandler *) IsJNG;
8004 entry->flags|=CoderDecoderSeekableStreamFlag;
8005 entry->flags^=CoderAdjoinFlag;
8006 entry->mime_type=ConstantString("image/x-jng");
8007 entry->note=ConstantString(JNGNote);
8008 (void) RegisterMagickInfo(entry);
8010 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8011 ping_semaphore=AcquireSemaphoreInfo();
8014 return(MagickImageCoderSignature);
8018 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8022 % U n r e g i s t e r P N G I m a g e %
8026 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8028 % UnregisterPNGImage() removes format registrations made by the
8029 % PNG module from the list of supported formats.
8031 % The format of the UnregisterPNGImage method is:
8033 % UnregisterPNGImage(void)
8036 ModuleExport void UnregisterPNGImage(void)
8038 (void) UnregisterMagickInfo("MNG");
8039 (void) UnregisterMagickInfo("PNG");
8040 (void) UnregisterMagickInfo("PNG8");
8041 (void) UnregisterMagickInfo("PNG24");
8042 (void) UnregisterMagickInfo("PNG32");
8043 (void) UnregisterMagickInfo("PNG48");
8044 (void) UnregisterMagickInfo("PNG64");
8045 (void) UnregisterMagickInfo("PNG00");
8046 (void) UnregisterMagickInfo("JNG");
8048 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8049 if (ping_semaphore != (SemaphoreInfo *) NULL)
8050 RelinquishSemaphoreInfo(&ping_semaphore);
8054 #if defined(MAGICKCORE_PNG_DELEGATE)
8055 #if PNG_LIBPNG_VER > 10011
8057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8061 % W r i t e M N G I m a g e %
8065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8067 % WriteMNGImage() writes an image in the Portable Network Graphics
8068 % Group's "Multiple-image Network Graphics" encoded image format.
8070 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
8072 % The format of the WriteMNGImage method is:
8074 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8075 % Image *image,ExceptionInfo *exception)
8077 % A description of each parameter follows.
8079 % o image_info: the image info.
8081 % o image: The image.
8083 % o exception: return any errors or warnings in this structure.
8085 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8086 % "To do" under ReadPNGImage):
8088 % Preserve all unknown and not-yet-handled known chunks found in input
8089 % PNG file and copy them into output PNG files according to the PNG
8092 % Write the iCCP chunk at MNG level when (icc profile length > 0)
8094 % Improve selection of color type (use indexed-colour or indexed-colour
8095 % with tRNS when 256 or fewer unique RGBA values are present).
8097 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8098 % This will be complicated if we limit ourselves to generating MNG-LC
8099 % files. For now we ignore disposal method 3 and simply overlay the next
8102 % Check for identical PLTE's or PLTE/tRNS combinations and use a
8103 % global MNG PLTE or PLTE/tRNS combination when appropriate.
8104 % [mostly done 15 June 1999 but still need to take care of tRNS]
8106 % Check for identical sRGB and replace with a global sRGB (and remove
8107 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8108 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8109 % local gAMA/cHRM with local sRGB if appropriate).
8111 % Check for identical sBIT chunks and write global ones.
8113 % Provide option to skip writing the signature tEXt chunks.
8115 % Use signatures to detect identical objects and reuse the first
8116 % instance of such objects instead of writing duplicate objects.
8118 % Use a smaller-than-32k value of compression window size when
8121 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
8122 % ancillary text chunks and save profiles.
8124 % Provide an option to force LC files (to ensure exact framing rate)
8127 % Provide an option to force VLC files instead of LC, even when offsets
8128 % are present. This will involve expanding the embedded images with a
8129 % transparent region at the top and/or left.
8133 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8134 png_info *ping_info, unsigned char *profile_type, unsigned char
8135 *profile_description, unsigned char *profile_data, png_uint_32 length)
8154 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8156 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8159 if (image_info->verbose)
8161 (void) printf("writing raw profile: type=%s, length=%.20g\n",
8162 (char *) profile_type, (double) length);
8165 #if PNG_LIBPNG_VER >= 10400
8166 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8168 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8170 description_length=(png_uint_32) strlen((const char *) profile_description);
8171 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8172 + description_length);
8173 #if PNG_LIBPNG_VER >= 10400
8174 text[0].text=(png_charp) png_malloc(ping,
8175 (png_alloc_size_t) allocated_length);
8176 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8178 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8179 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8181 text[0].key[0]='\0';
8182 (void) ConcatenateMagickString(text[0].key,
8183 "Raw profile type ",MagickPathExtent);
8184 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8188 (void) CopyMagickString(dp,(const char *) profile_description,
8190 dp+=description_length;
8192 (void) FormatLocaleString(dp,allocated_length-
8193 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8196 for (i=0; i < (ssize_t) length; i++)
8200 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8201 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8206 text[0].text_length=(png_size_t) (dp-text[0].text);
8207 text[0].compression=image_info->compression == NoCompression ||
8208 (image_info->compression == UndefinedCompression &&
8209 text[0].text_length < 128) ? -1 : 0;
8211 if (text[0].text_length <= allocated_length)
8212 png_set_text(ping,ping_info,text,1);
8214 png_free(ping,text[0].text);
8215 png_free(ping,text[0].key);
8216 png_free(ping,text);
8219 static inline MagickBooleanType IsColorEqual(const Image *image,
8220 const Quantum *p, const PixelInfo *q)
8227 red=(MagickRealType) GetPixelRed(image,p);
8228 green=(MagickRealType) GetPixelGreen(image,p);
8229 blue=(MagickRealType) GetPixelBlue(image,p);
8230 if ((AbsolutePixelValue(red-q->red) < MagickEpsilon) &&
8231 (AbsolutePixelValue(green-q->green) < MagickEpsilon) &&
8232 (AbsolutePixelValue(blue-q->blue) < MagickEpsilon))
8234 return(MagickFalse);
8237 #if defined(PNG_tIME_SUPPORTED)
8238 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8239 const char *timestamp,ExceptionInfo *exception)
8259 assert(timestamp != (const char *) NULL);
8260 LogMagickEvent(CoderEvent,GetMagickModule(),
8261 " Writing tIME chunk: timestamp property is %30s\n",timestamp);
8262 ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d",&year,&month,&day,&hour,
8266 ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d%d:%d",&year,&month,&day,&hour,
8267 &minute, &second, &addhours, &addminutes);
8268 LogMagickEvent(CoderEvent,GetMagickModule(),
8269 " Date format specified for png:tIME=%s" ,timestamp);
8270 LogMagickEvent(CoderEvent,GetMagickModule(),
8271 " ret=%d,y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, as=%d",
8272 ret,year,month,day,hour,minute,second,addhours,addminutes);
8275 LogMagickEvent(CoderEvent,GetMagickModule(),
8276 " Invalid date, ret=%d",ret);
8277 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8278 "Invalid date format specified for png:tIME","`%s' (ret=%d)",
8279 image->filename,ret);
8285 addminutes=-addminutes;
8298 if(month == 4 || month == 6 || month == 9 || month == 11)
8324 /* To do: fix this for leap years */
8325 if (day > 31 || (month == 2 && day > 28) || ((month == 4 || month == 6 ||
8326 month == 9 || month == 11) && day > 30))
8338 ptime.month = month;
8341 ptime.minute = minute;
8342 ptime.second = second;
8344 LogMagickEvent(CoderEvent,GetMagickModule(),
8345 " png_set_tIME: y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, am=%d",
8346 ptime.year, ptime.month, ptime.day, ptime.hour, ptime.minute,
8347 ptime.second, addhours, addminutes);
8348 png_set_tIME(ping,info,&ptime);
8352 /* Write one PNG image */
8353 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8354 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8386 ping_trans_alpha[256];
8414 ping_have_cheap_transparency,
8428 /* ping_exclude_EXIF, */
8432 /* ping_exclude_iTXt, */
8438 /* ping_exclude_tRNS, */
8440 ping_exclude_zCCP, /* hex-encoded iCCP */
8443 ping_preserve_colormap,
8445 ping_need_colortype_warning,
8453 *volatile pixel_info;
8472 ping_interlace_method,
8473 ping_compression_method,
8490 number_semitransparent,
8492 ping_pHYs_unit_type;
8495 ping_pHYs_x_resolution,
8496 ping_pHYs_y_resolution;
8498 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8499 " Enter WriteOnePNGImage()");
8501 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8502 if (image == (Image *) NULL)
8503 return(MagickFalse);
8504 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8506 /* Define these outside of the following "if logging()" block so they will
8507 * show in debuggers.
8510 (void) ConcatenateMagickString(im_vers,
8511 MagickLibVersionText,MagickPathExtent);
8512 (void) ConcatenateMagickString(im_vers,
8513 MagickLibAddendum,MagickPathExtent);
8516 (void) ConcatenateMagickString(libpng_vers,
8517 PNG_LIBPNG_VER_STRING,32);
8519 (void) ConcatenateMagickString(libpng_runv,
8520 png_get_libpng_ver(NULL),32);
8523 (void) ConcatenateMagickString(zlib_vers,
8526 (void) ConcatenateMagickString(zlib_runv,
8529 if (logging != MagickFalse)
8531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8532 " IM version = %s", im_vers);
8533 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8534 " Libpng version = %s", libpng_vers);
8535 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8538 " running with %s", libpng_runv);
8540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8541 " Zlib version = %s", zlib_vers);
8542 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8545 " running with %s", zlib_runv);
8549 /* Initialize some stuff */
8552 ping_interlace_method=0,
8553 ping_compression_method=0,
8554 ping_filter_method=0,
8557 ping_background.red = 0;
8558 ping_background.green = 0;
8559 ping_background.blue = 0;
8560 ping_background.gray = 0;
8561 ping_background.index = 0;
8563 ping_trans_color.red=0;
8564 ping_trans_color.green=0;
8565 ping_trans_color.blue=0;
8566 ping_trans_color.gray=0;
8568 ping_pHYs_unit_type = 0;
8569 ping_pHYs_x_resolution = 0;
8570 ping_pHYs_y_resolution = 0;
8572 ping_have_blob=MagickFalse;
8573 ping_have_cheap_transparency=MagickFalse;
8574 ping_have_color=MagickTrue;
8575 ping_have_non_bw=MagickTrue;
8576 ping_have_PLTE=MagickFalse;
8577 ping_have_bKGD=MagickFalse;
8578 ping_have_eXIf=MagickTrue;
8579 ping_have_iCCP=MagickFalse;
8580 ping_have_pHYs=MagickFalse;
8581 ping_have_sRGB=MagickFalse;
8582 ping_have_tRNS=MagickFalse;
8584 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8585 ping_exclude_caNv=mng_info->ping_exclude_caNv;
8586 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8587 ping_exclude_date=mng_info->ping_exclude_date;
8588 ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8589 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8590 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8591 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8592 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8593 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8594 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8595 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8596 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8597 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8598 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8599 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8601 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8602 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8603 ping_need_colortype_warning = MagickFalse;
8605 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8606 * i.e., eliminate the ICC profile and set image->rendering_intent.
8607 * Note that this will not involve any changes to the actual pixels
8608 * but merely passes information to applications that read the resulting
8611 * To do: recognize other variants of the sRGB profile, using the CRC to
8612 * verify all recognized variants including the 7 already known.
8614 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8616 * Use something other than image->rendering_intent to record the fact
8617 * that the sRGB profile was found.
8619 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8620 * profile. Record the Blackpoint Compensation, if any.
8622 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8624 ResetImageProfileIterator(image);
8625 for (name=GetNextImageProfile(image); name != (char *) NULL; )
8627 profile=GetImageProfile(image,name);
8629 if (profile != (StringInfo *) NULL)
8631 if ((LocaleCompare(name,"ICC") == 0) ||
8632 (LocaleCompare(name,"ICM") == 0))
8647 length=(png_uint_32) GetStringInfoLength(profile);
8649 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8651 if (length == sRGB_info[icheck].len)
8655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8656 " Got a %lu-byte ICC profile (potentially sRGB)",
8657 (unsigned long) length);
8659 data=GetStringInfoDatum(profile);
8660 profile_crc=crc32(0,data,length);
8662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8663 " with crc=%8x",(unsigned int) profile_crc);
8667 if (profile_crc == sRGB_info[icheck].crc)
8669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8670 " It is sRGB with rendering intent = %s",
8671 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8672 sRGB_info[icheck].intent));
8673 if (image->rendering_intent==UndefinedIntent)
8675 image->rendering_intent=
8676 Magick_RenderingIntent_from_PNG_RenderingIntent(
8677 sRGB_info[icheck].intent);
8679 ping_exclude_iCCP = MagickTrue;
8680 ping_exclude_zCCP = MagickTrue;
8681 ping_have_sRGB = MagickTrue;
8686 if (sRGB_info[icheck].len == 0)
8687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8688 " Got %lu-byte ICC profile not recognized as sRGB",
8689 (unsigned long) length);
8692 name=GetNextImageProfile(image);
8697 number_semitransparent = 0;
8698 number_transparent = 0;
8700 if (logging != MagickFalse)
8702 if (image->storage_class == UndefinedClass)
8703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8704 " image->storage_class=UndefinedClass");
8705 if (image->storage_class == DirectClass)
8706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707 " image->storage_class=DirectClass");
8708 if (image->storage_class == PseudoClass)
8709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8710 " image->storage_class=PseudoClass");
8711 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8712 " image->taint=MagickTrue":
8713 " image->taint=MagickFalse");
8714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8715 " image->gamma=%g", image->gamma);
8718 if (image->storage_class == PseudoClass &&
8719 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8720 mng_info->write_png48 || mng_info->write_png64 ||
8721 (mng_info->write_png_colortype != 1 &&
8722 mng_info->write_png_colortype != 5)))
8724 (void) SyncImage(image,exception);
8725 image->storage_class = DirectClass;
8728 if (ping_preserve_colormap == MagickFalse)
8730 if (image->storage_class != PseudoClass && image->colormap != NULL)
8732 /* Free the bogus colormap; it can cause trouble later */
8733 if (logging != MagickFalse)
8734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8735 " Freeing bogus colormap");
8736 (void) RelinquishMagickMemory(image->colormap);
8737 image->colormap=NULL;
8741 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8742 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8745 Sometimes we get PseudoClass images whose RGB values don't match
8746 the colors in the colormap. This code syncs the RGB values.
8748 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8749 (void) SyncImage(image,exception);
8751 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8752 if (image->depth > 8)
8754 if (logging != MagickFalse)
8755 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8756 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8762 /* Respect the -depth option */
8763 if (image->depth < 4)
8768 if (image->depth > 2)
8770 /* Scale to 4-bit */
8771 LBR04PacketRGBA(image->background_color);
8773 for (y=0; y < (ssize_t) image->rows; y++)
8775 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8777 if (r == (Quantum *) NULL)
8780 for (x=0; x < (ssize_t) image->columns; x++)
8783 r+=GetPixelChannels(image);
8786 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8790 if (image->storage_class == PseudoClass && image->colormap != NULL)
8792 for (i=0; i < (ssize_t) image->colors; i++)
8794 LBR04PacketRGBA(image->colormap[i]);
8798 else if (image->depth > 1)
8800 /* Scale to 2-bit */
8801 LBR02PacketRGBA(image->background_color);
8803 for (y=0; y < (ssize_t) image->rows; y++)
8805 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8807 if (r == (Quantum *) NULL)
8810 for (x=0; x < (ssize_t) image->columns; x++)
8813 r+=GetPixelChannels(image);
8816 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8820 if (image->storage_class == PseudoClass && image->colormap != NULL)
8822 for (i=0; i < (ssize_t) image->colors; i++)
8824 LBR02PacketRGBA(image->colormap[i]);
8830 /* Scale to 1-bit */
8831 LBR01PacketRGBA(image->background_color);
8833 for (y=0; y < (ssize_t) image->rows; y++)
8835 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8837 if (r == (Quantum *) NULL)
8840 for (x=0; x < (ssize_t) image->columns; x++)
8843 r+=GetPixelChannels(image);
8846 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8850 if (image->storage_class == PseudoClass && image->colormap != NULL)
8852 for (i=0; i < (ssize_t) image->colors; i++)
8854 LBR01PacketRGBA(image->colormap[i]);
8860 /* To do: set to next higher multiple of 8 */
8861 if (image->depth < 8)
8864 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8865 /* PNG does not handle depths greater than 16 so reduce it even
8868 if (image->depth > 8)
8872 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8873 if (image->depth > 8)
8875 /* To do: fill low byte properly */
8879 if (image->depth == 16 && mng_info->write_png_depth != 16)
8880 if (mng_info->write_png8 ||
8881 LosslessReduceDepthOK(image,exception) != MagickFalse)
8885 image_colors = (int) image->colors;
8886 number_opaque = (int) image->colors;
8887 number_transparent = 0;
8888 number_semitransparent = 0;
8890 if (mng_info->write_png_colortype &&
8891 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8892 mng_info->write_png_colortype < 4 &&
8893 image->alpha_trait == UndefinedPixelTrait)))
8895 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8896 * are not going to need the result.
8898 if (mng_info->write_png_colortype == 1 ||
8899 mng_info->write_png_colortype == 5)
8900 ping_have_color=MagickFalse;
8902 if (image->alpha_trait != UndefinedPixelTrait)
8904 number_transparent = 2;
8905 number_semitransparent = 1;
8909 if (mng_info->write_png_colortype < 7)
8913 * Normally we run this just once, but in the case of writing PNG8
8914 * we reduce the transparency to binary and run again, then if there
8915 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8916 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8917 * palette. Then (To do) we take care of a final reduction that is only
8918 * needed if there are still 256 colors present and one of them has both
8919 * transparent and opaque instances.
8922 tried_332 = MagickFalse;
8923 tried_333 = MagickFalse;
8924 tried_444 = MagickFalse;
8926 image->depth=GetImageDepth(image,exception);
8930 * Sometimes we get DirectClass images that have 256 colors or fewer.
8931 * This code will build a colormap.
8933 * Also, sometimes we get PseudoClass images with an out-of-date
8934 * colormap. This code will replace the colormap with a new one.
8935 * Sometimes we get PseudoClass images that have more than 256 colors.
8936 * This code will delete the colormap and change the image to
8939 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8940 * even though it sometimes contains left-over non-opaque values.
8942 * Also we gather some information (number of opaque, transparent,
8943 * and semitransparent pixels, and whether the image has any non-gray
8944 * pixels or only black-and-white pixels) that we might need later.
8946 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8947 * we need to check for bogus non-opaque values, at least.
8955 semitransparent[260],
8958 register const Quantum
8964 if (logging != MagickFalse)
8965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8966 " Enter BUILD_PALETTE:");
8968 if (logging != MagickFalse)
8970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8971 " image->columns=%.20g",(double) image->columns);
8972 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8973 " image->rows=%.20g",(double) image->rows);
8974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8975 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8977 " image->depth=%.20g",(double) image->depth);
8979 if (image->storage_class == PseudoClass && image->colormap != NULL)
8981 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8982 " Original colormap:");
8983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8984 " i (red,green,blue,alpha)");
8986 for (i=0; i < 256; i++)
8988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8989 " %d (%d,%d,%d,%d)",
8991 (int) image->colormap[i].red,
8992 (int) image->colormap[i].green,
8993 (int) image->colormap[i].blue,
8994 (int) image->colormap[i].alpha);
8997 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
9001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002 " %d (%d,%d,%d,%d)",
9004 (int) image->colormap[i].red,
9005 (int) image->colormap[i].green,
9006 (int) image->colormap[i].blue,
9007 (int) image->colormap[i].alpha);
9012 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9013 " image->colors=%d",(int) image->colors);
9015 if (image->colors == 0)
9016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9017 " (zero means unknown)");
9019 if (ping_preserve_colormap == MagickFalse)
9020 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9021 " Regenerate the colormap");
9026 number_semitransparent = 0;
9027 number_transparent = 0;
9029 for (y=0; y < (ssize_t) image->rows; y++)
9031 r=GetVirtualPixels(image,0,y,image->columns,1,exception);
9033 if (r == (const Quantum *) NULL)
9036 for (x=0; x < (ssize_t) image->columns; x++)
9038 if (image->alpha_trait == UndefinedPixelTrait ||
9039 GetPixelAlpha(image,r) == OpaqueAlpha)
9041 if (number_opaque < 259)
9043 if (number_opaque == 0)
9045 GetPixelInfoPixel(image,r,opaque);
9046 opaque[0].alpha=OpaqueAlpha;
9050 for (i=0; i< (ssize_t) number_opaque; i++)
9052 if (IsColorEqual(image,r,opaque+i))
9056 if (i == (ssize_t) number_opaque && number_opaque < 259)
9059 GetPixelInfoPixel(image,r,opaque+i);
9060 opaque[i].alpha=OpaqueAlpha;
9064 else if (GetPixelAlpha(image,r) == TransparentAlpha)
9066 if (number_transparent < 259)
9068 if (number_transparent == 0)
9070 GetPixelInfoPixel(image,r,transparent);
9071 ping_trans_color.red=(unsigned short)
9072 GetPixelRed(image,r);
9073 ping_trans_color.green=(unsigned short)
9074 GetPixelGreen(image,r);
9075 ping_trans_color.blue=(unsigned short)
9076 GetPixelBlue(image,r);
9077 ping_trans_color.gray=(unsigned short)
9078 GetPixelGray(image,r);
9079 number_transparent = 1;
9082 for (i=0; i< (ssize_t) number_transparent; i++)
9084 if (IsColorEqual(image,r,transparent+i))
9088 if (i == (ssize_t) number_transparent &&
9089 number_transparent < 259)
9091 number_transparent++;
9092 GetPixelInfoPixel(image,r,transparent+i);
9098 if (number_semitransparent < 259)
9100 if (number_semitransparent == 0)
9102 GetPixelInfoPixel(image,r,semitransparent);
9103 number_semitransparent = 1;
9106 for (i=0; i< (ssize_t) number_semitransparent; i++)
9108 if (IsColorEqual(image,r,semitransparent+i)
9109 && GetPixelAlpha(image,r) ==
9110 semitransparent[i].alpha)
9114 if (i == (ssize_t) number_semitransparent &&
9115 number_semitransparent < 259)
9117 number_semitransparent++;
9118 GetPixelInfoPixel(image,r,semitransparent+i);
9122 r+=GetPixelChannels(image);
9126 if (mng_info->write_png8 == MagickFalse &&
9127 ping_exclude_bKGD == MagickFalse)
9129 /* Add the background color to the palette, if it
9130 * isn't already there.
9132 if (logging != MagickFalse)
9134 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9135 " Check colormap for background (%d,%d,%d)",
9136 (int) image->background_color.red,
9137 (int) image->background_color.green,
9138 (int) image->background_color.blue);
9140 for (i=0; i<number_opaque; i++)
9142 if (opaque[i].red == image->background_color.red &&
9143 opaque[i].green == image->background_color.green &&
9144 opaque[i].blue == image->background_color.blue)
9147 if (number_opaque < 259 && i == number_opaque)
9149 opaque[i] = image->background_color;
9150 ping_background.index = i;
9152 if (logging != MagickFalse)
9154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155 " background_color index is %d",(int) i);
9159 else if (logging != MagickFalse)
9160 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9161 " No room in the colormap to add background color");
9164 image_colors=number_opaque+number_transparent+number_semitransparent;
9166 if (logging != MagickFalse)
9168 if (image_colors > 256)
9169 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9170 " image has more than 256 colors");
9173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9174 " image has %d colors",image_colors);
9177 if (ping_preserve_colormap != MagickFalse)
9180 if (mng_info->write_png_colortype != 7) /* We won't need this info */
9182 ping_have_color=MagickFalse;
9183 ping_have_non_bw=MagickFalse;
9185 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9188 "incompatible colorspace");
9189 ping_have_color=MagickTrue;
9190 ping_have_non_bw=MagickTrue;
9193 if(image_colors > 256)
9195 for (y=0; y < (ssize_t) image->rows; y++)
9197 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9199 if (q == (Quantum *) NULL)
9203 for (x=0; x < (ssize_t) image->columns; x++)
9205 if (GetPixelRed(image,r) != GetPixelGreen(image,r) ||
9206 GetPixelRed(image,r) != GetPixelBlue(image,r))
9208 ping_have_color=MagickTrue;
9209 ping_have_non_bw=MagickTrue;
9212 r+=GetPixelChannels(image);
9215 if (ping_have_color != MagickFalse)
9218 /* Worst case is black-and-white; we are looking at every
9222 if (ping_have_non_bw == MagickFalse)
9225 for (x=0; x < (ssize_t) image->columns; x++)
9227 if (GetPixelRed(image,r) != 0 &&
9228 GetPixelRed(image,r) != QuantumRange)
9230 ping_have_non_bw=MagickTrue;
9233 r+=GetPixelChannels(image);
9240 if (image_colors < 257)
9246 * Initialize image colormap.
9249 if (logging != MagickFalse)
9250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9251 " Sort the new colormap");
9253 /* Sort palette, transparent first */;
9257 for (i=0; i<number_transparent; i++)
9258 colormap[n++] = transparent[i];
9260 for (i=0; i<number_semitransparent; i++)
9261 colormap[n++] = semitransparent[i];
9263 for (i=0; i<number_opaque; i++)
9264 colormap[n++] = opaque[i];
9266 ping_background.index +=
9267 (number_transparent + number_semitransparent);
9269 /* image_colors < 257; search the colormap instead of the pixels
9270 * to get ping_have_color and ping_have_non_bw
9274 if (ping_have_color == MagickFalse)
9276 if (colormap[i].red != colormap[i].green ||
9277 colormap[i].red != colormap[i].blue)
9279 ping_have_color=MagickTrue;
9280 ping_have_non_bw=MagickTrue;
9285 if (ping_have_non_bw == MagickFalse)
9287 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9288 ping_have_non_bw=MagickTrue;
9292 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9293 (number_transparent == 0 && number_semitransparent == 0)) &&
9294 (((mng_info->write_png_colortype-1) ==
9295 PNG_COLOR_TYPE_PALETTE) ||
9296 (mng_info->write_png_colortype == 0)))
9298 if (logging != MagickFalse)
9300 if (n != (ssize_t) image_colors)
9301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302 " image_colors (%d) and n (%d) don't match",
9305 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306 " AcquireImageColormap");
9309 image->colors = image_colors;
9311 if (AcquireImageColormap(image,image_colors,exception) == MagickFalse)
9313 (void) ThrowMagickException(exception,GetMagickModule(),
9314 ResourceLimitError,"MemoryAllocationFailed","`%s'",
9319 for (i=0; i< (ssize_t) image_colors; i++)
9320 image->colormap[i] = colormap[i];
9322 if (logging != MagickFalse)
9324 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9325 " image->colors=%d (%d)",
9326 (int) image->colors, image_colors);
9328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9329 " Update the pixel indexes");
9332 /* Sync the pixel indices with the new colormap */
9334 for (y=0; y < (ssize_t) image->rows; y++)
9336 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9338 if (q == (Quantum *) NULL)
9341 for (x=0; x < (ssize_t) image->columns; x++)
9343 for (i=0; i< (ssize_t) image_colors; i++)
9345 if ((image->alpha_trait == UndefinedPixelTrait ||
9346 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9347 image->colormap[i].red == GetPixelRed(image,q) &&
9348 image->colormap[i].green == GetPixelGreen(image,q) &&
9349 image->colormap[i].blue == GetPixelBlue(image,q))
9351 SetPixelIndex(image,i,q);
9355 q+=GetPixelChannels(image);
9358 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9364 if (logging != MagickFalse)
9366 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9367 " image->colors=%d", (int) image->colors);
9369 if (image->colormap != NULL)
9371 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9372 " i (red,green,blue,alpha)");
9374 for (i=0; i < (ssize_t) image->colors; i++)
9376 if (i < 300 || i >= (ssize_t) image->colors - 10)
9378 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9379 " %d (%d,%d,%d,%d)",
9381 (int) image->colormap[i].red,
9382 (int) image->colormap[i].green,
9383 (int) image->colormap[i].blue,
9384 (int) image->colormap[i].alpha);
9389 if (number_transparent < 257)
9390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9391 " number_transparent = %d",
9392 number_transparent);
9395 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9396 " number_transparent > 256");
9398 if (number_opaque < 257)
9399 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9400 " number_opaque = %d",
9404 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9405 " number_opaque > 256");
9407 if (number_semitransparent < 257)
9408 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9409 " number_semitransparent = %d",
9410 number_semitransparent);
9413 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9414 " number_semitransparent > 256");
9416 if (ping_have_non_bw == MagickFalse)
9417 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9418 " All pixels and the background are black or white");
9420 else if (ping_have_color == MagickFalse)
9421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9422 " All pixels and the background are gray");
9425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9426 " At least one pixel or the background is non-gray");
9428 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9429 " Exit BUILD_PALETTE:");
9432 if (mng_info->write_png8 == MagickFalse)
9435 /* Make any reductions necessary for the PNG8 format */
9436 if (image_colors <= 256 &&
9437 image_colors != 0 && image->colormap != NULL &&
9438 number_semitransparent == 0 &&
9439 number_transparent <= 1)
9442 /* PNG8 can't have semitransparent colors so we threshold the
9443 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9444 * transparent color so if more than one is transparent we merge
9445 * them into image->background_color.
9447 if (number_semitransparent != 0 || number_transparent > 1)
9449 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9450 " Thresholding the alpha channel to binary");
9452 for (y=0; y < (ssize_t) image->rows; y++)
9454 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9456 if (q == (Quantum *) NULL)
9459 for (x=0; x < (ssize_t) image->columns; x++)
9461 if (GetPixelAlpha(image,q) < OpaqueAlpha/2)
9463 SetPixelViaPixelInfo(image,&image->background_color,q);
9464 SetPixelAlpha(image,TransparentAlpha,q);
9467 SetPixelAlpha(image,OpaqueAlpha,q);
9468 q+=GetPixelChannels(image);
9471 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9474 if (image_colors != 0 && image_colors <= 256 &&
9475 image->colormap != NULL)
9476 for (i=0; i<image_colors; i++)
9477 image->colormap[i].alpha =
9478 (image->colormap[i].alpha > TransparentAlpha/2 ?
9479 TransparentAlpha : OpaqueAlpha);
9484 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9485 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9486 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9489 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9491 if (logging != MagickFalse)
9492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9493 " Quantizing the background color to 4-4-4");
9495 tried_444 = MagickTrue;
9497 LBR04PacketRGB(image->background_color);
9499 if (logging != MagickFalse)
9500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9501 " Quantizing the pixel colors to 4-4-4");
9503 if (image->colormap == NULL)
9505 for (y=0; y < (ssize_t) image->rows; y++)
9507 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9509 if (q == (Quantum *) NULL)
9512 for (x=0; x < (ssize_t) image->columns; x++)
9514 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9516 q+=GetPixelChannels(image);
9519 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9524 else /* Should not reach this; colormap already exists and
9527 if (logging != MagickFalse)
9528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9529 " Quantizing the colormap to 4-4-4");
9531 for (i=0; i<image_colors; i++)
9533 LBR04PacketRGB(image->colormap[i]);
9539 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9541 if (logging != MagickFalse)
9542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9543 " Quantizing the background color to 3-3-3");
9545 tried_333 = MagickTrue;
9547 LBR03PacketRGB(image->background_color);
9549 if (logging != MagickFalse)
9550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9551 " Quantizing the pixel colors to 3-3-3-1");
9553 if (image->colormap == NULL)
9555 for (y=0; y < (ssize_t) image->rows; y++)
9557 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9559 if (q == (Quantum *) NULL)
9562 for (x=0; x < (ssize_t) image->columns; x++)
9564 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9566 q+=GetPixelChannels(image);
9569 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9574 else /* Should not reach this; colormap already exists and
9577 if (logging != MagickFalse)
9578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9579 " Quantizing the colormap to 3-3-3-1");
9580 for (i=0; i<image_colors; i++)
9582 LBR03PacketRGB(image->colormap[i]);
9588 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9590 if (logging != MagickFalse)
9591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9592 " Quantizing the background color to 3-3-2");
9594 tried_332 = MagickTrue;
9596 /* Red and green were already done so we only quantize the blue
9600 LBR02PacketBlue(image->background_color);
9602 if (logging != MagickFalse)
9603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9604 " Quantizing the pixel colors to 3-3-2-1");
9606 if (image->colormap == NULL)
9608 for (y=0; y < (ssize_t) image->rows; y++)
9610 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9612 if (q == (Quantum *) NULL)
9615 for (x=0; x < (ssize_t) image->columns; x++)
9617 if (GetPixelAlpha(image,q) == OpaqueAlpha)
9619 q+=GetPixelChannels(image);
9622 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9627 else /* Should not reach this; colormap already exists and
9630 if (logging != MagickFalse)
9631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9632 " Quantizing the colormap to 3-3-2-1");
9633 for (i=0; i<image_colors; i++)
9635 LBR02PacketBlue(image->colormap[i]);
9641 if (image_colors == 0 || image_colors > 256)
9643 /* Take care of special case with 256 opaque colors + 1 transparent
9644 * color. We don't need to quantize to 2-3-2-1; we only need to
9645 * eliminate one color, so we'll merge the two darkest red
9646 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9648 if (logging != MagickFalse)
9649 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9650 " Merging two dark red background colors to 3-3-2-1");
9652 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9653 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9654 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9656 image->background_color.red=ScaleCharToQuantum(0x24);
9659 if (logging != MagickFalse)
9660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9661 " Merging two dark red pixel colors to 3-3-2-1");
9663 if (image->colormap == NULL)
9665 for (y=0; y < (ssize_t) image->rows; y++)
9667 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9669 if (q == (Quantum *) NULL)
9672 for (x=0; x < (ssize_t) image->columns; x++)
9674 if (ScaleQuantumToChar(GetPixelRed(image,q)) == 0x49 &&
9675 ScaleQuantumToChar(GetPixelGreen(image,q)) == 0x00 &&
9676 ScaleQuantumToChar(GetPixelBlue(image,q)) == 0x00 &&
9677 GetPixelAlpha(image,q) == OpaqueAlpha)
9679 SetPixelRed(image,ScaleCharToQuantum(0x24),q);
9681 q+=GetPixelChannels(image);
9684 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9692 for (i=0; i<image_colors; i++)
9694 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9695 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9696 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9698 image->colormap[i].red=ScaleCharToQuantum(0x24);
9705 /* END OF BUILD_PALETTE */
9707 /* If we are excluding the tRNS chunk and there is transparency,
9708 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9711 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9712 (number_transparent != 0 || number_semitransparent != 0))
9714 unsigned int colortype=mng_info->write_png_colortype;
9716 if (ping_have_color == MagickFalse)
9717 mng_info->write_png_colortype = 5;
9720 mng_info->write_png_colortype = 7;
9722 if (colortype != 0 &&
9723 mng_info->write_png_colortype != colortype)
9724 ping_need_colortype_warning=MagickTrue;
9728 /* See if cheap transparency is possible. It is only possible
9729 * when there is a single transparent color, no semitransparent
9730 * color, and no opaque color that has the same RGB components
9731 * as the transparent color. We only need this information if
9732 * we are writing a PNG with colortype 0 or 2, and we have not
9733 * excluded the tRNS chunk.
9735 if (number_transparent == 1 &&
9736 mng_info->write_png_colortype < 4)
9738 ping_have_cheap_transparency = MagickTrue;
9740 if (number_semitransparent != 0)
9741 ping_have_cheap_transparency = MagickFalse;
9743 else if (image_colors == 0 || image_colors > 256 ||
9744 image->colormap == NULL)
9746 register const Quantum
9749 for (y=0; y < (ssize_t) image->rows; y++)
9751 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9753 if (q == (Quantum *) NULL)
9756 for (x=0; x < (ssize_t) image->columns; x++)
9758 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9759 (unsigned short) GetPixelRed(image,q) ==
9760 ping_trans_color.red &&
9761 (unsigned short) GetPixelGreen(image,q) ==
9762 ping_trans_color.green &&
9763 (unsigned short) GetPixelBlue(image,q) ==
9764 ping_trans_color.blue)
9766 ping_have_cheap_transparency = MagickFalse;
9770 q+=GetPixelChannels(image);
9773 if (ping_have_cheap_transparency == MagickFalse)
9779 /* Assuming that image->colormap[0] is the one transparent color
9780 * and that all others are opaque.
9782 if (image_colors > 1)
9783 for (i=1; i<image_colors; i++)
9784 if (image->colormap[i].red == image->colormap[0].red &&
9785 image->colormap[i].green == image->colormap[0].green &&
9786 image->colormap[i].blue == image->colormap[0].blue)
9788 ping_have_cheap_transparency = MagickFalse;
9793 if (logging != MagickFalse)
9795 if (ping_have_cheap_transparency == MagickFalse)
9796 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9797 " Cheap transparency is not possible.");
9800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9801 " Cheap transparency is possible.");
9805 ping_have_cheap_transparency = MagickFalse;
9807 image_depth=image->depth;
9809 quantum_info = (QuantumInfo *) NULL;
9811 image_colors=(int) image->colors;
9812 image_matte=image->alpha_trait !=
9813 UndefinedPixelTrait ? MagickTrue : MagickFalse;
9815 if (mng_info->write_png_colortype < 5)
9816 mng_info->IsPalette=image->storage_class == PseudoClass &&
9817 image_colors <= 256 && image->colormap != NULL;
9819 mng_info->IsPalette = MagickFalse;
9821 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9822 (image->colors == 0 || image->colormap == NULL))
9824 image_info=DestroyImageInfo(image_info);
9825 image=DestroyImage(image);
9826 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9827 "Cannot write PNG8 or color-type 3; colormap is NULL",
9828 "`%s'",IMimage->filename);
9829 return(MagickFalse);
9833 Allocate the PNG structures
9835 #ifdef PNG_USER_MEM_SUPPORTED
9836 error_info.image=image;
9837 error_info.exception=exception;
9838 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9839 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9840 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9843 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9844 MagickPNGErrorHandler,MagickPNGWarningHandler);
9847 if (ping == (png_struct *) NULL)
9848 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9850 ping_info=png_create_info_struct(ping);
9852 if (ping_info == (png_info *) NULL)
9854 png_destroy_write_struct(&ping,(png_info **) NULL);
9855 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9858 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9859 pixel_info=(MemoryInfo *) NULL;
9861 if (setjmp(png_jmpbuf(ping)))
9867 if (image_info->verbose)
9868 (void) printf("PNG write has failed.\n");
9870 png_destroy_write_struct(&ping,&ping_info);
9871 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9872 UnlockSemaphoreInfo(ping_semaphore);
9875 if (pixel_info != (MemoryInfo *) NULL)
9876 pixel_info=RelinquishVirtualMemory(pixel_info);
9878 if (quantum_info != (QuantumInfo *) NULL)
9879 quantum_info=DestroyQuantumInfo(quantum_info);
9881 if (ping_have_blob != MagickFalse)
9882 (void) CloseBlob(image);
9883 image_info=DestroyImageInfo(image_info);
9884 image=DestroyImage(image);
9885 return(MagickFalse);
9888 /* { For navigation to end of SETJMP-protected block. Within this
9889 * block, use png_error() instead of Throwing an Exception, to ensure
9890 * that libpng is able to clean up, and that the semaphore is unlocked.
9893 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9894 LockSemaphoreInfo(ping_semaphore);
9897 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9898 /* Allow benign errors */
9899 png_set_benign_errors(ping, 1);
9902 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9903 /* Reject images with too many rows or columns */
9904 png_set_user_limits(ping,
9905 (png_uint_32) MagickMin(0x7fffffffL,
9906 GetMagickResourceLimit(WidthResource)),
9907 (png_uint_32) MagickMin(0x7fffffffL,
9908 GetMagickResourceLimit(HeightResource)));
9909 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9912 Prepare PNG for writing.
9915 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9916 if (mng_info->write_mng)
9918 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9919 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9920 /* Disable new libpng-1.5.10 feature when writing a MNG because
9921 * zero-length PLTE is OK
9923 png_set_check_for_invalid_index (ping, 0);
9928 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9929 if (mng_info->write_mng)
9930 png_permit_empty_plte(ping,MagickTrue);
9937 ping_width=(png_uint_32) image->columns;
9938 ping_height=(png_uint_32) image->rows;
9940 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9943 if (mng_info->write_png48 || mng_info->write_png64)
9946 if (mng_info->write_png_depth != 0)
9947 image_depth=mng_info->write_png_depth;
9949 /* Adjust requested depth to next higher valid depth if necessary */
9950 if (image_depth > 8)
9953 if ((image_depth > 4) && (image_depth < 8))
9956 if (image_depth == 3)
9959 if (logging != MagickFalse)
9961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9962 " width=%.20g",(double) ping_width);
9963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9964 " height=%.20g",(double) ping_height);
9965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9966 " image_matte=%.20g",(double) image->alpha_trait);
9967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9968 " image->depth=%.20g",(double) image->depth);
9969 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9970 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9973 save_image_depth=image_depth;
9974 ping_bit_depth=(png_byte) save_image_depth;
9977 #if defined(PNG_pHYs_SUPPORTED)
9978 if (ping_exclude_pHYs == MagickFalse)
9980 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9981 (!mng_info->write_mng || !mng_info->equal_physs))
9983 if (logging != MagickFalse)
9984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9985 " Setting up pHYs chunk");
9987 if (image->units == PixelsPerInchResolution)
9989 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9990 ping_pHYs_x_resolution=
9991 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9992 ping_pHYs_y_resolution=
9993 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9996 else if (image->units == PixelsPerCentimeterResolution)
9998 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9999 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
10000 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
10005 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
10006 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
10007 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
10010 if (logging != MagickFalse)
10011 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10012 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
10013 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
10014 (int) ping_pHYs_unit_type);
10015 ping_have_pHYs = MagickTrue;
10020 if (ping_exclude_bKGD == MagickFalse)
10022 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
10028 if (ping_bit_depth == 8)
10031 if (ping_bit_depth == 4)
10034 if (ping_bit_depth == 2)
10037 if (ping_bit_depth == 1)
10040 ping_background.red=(png_uint_16)
10041 (ScaleQuantumToShort(image->background_color.red) & mask);
10043 ping_background.green=(png_uint_16)
10044 (ScaleQuantumToShort(image->background_color.green) & mask);
10046 ping_background.blue=(png_uint_16)
10047 (ScaleQuantumToShort(image->background_color.blue) & mask);
10049 ping_background.gray=(png_uint_16) ping_background.green;
10052 if (logging != MagickFalse)
10054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10055 " Setting up bKGD chunk (1)");
10056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10057 " background_color index is %d",
10058 (int) ping_background.index);
10060 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10061 " ping_bit_depth=%d",ping_bit_depth);
10064 ping_have_bKGD = MagickTrue;
10068 Select the color type.
10073 if (mng_info->IsPalette && mng_info->write_png8)
10075 /* To do: make this a function cause it's used twice, except
10076 for reducing the sample depth from 8. */
10078 number_colors=image_colors;
10080 ping_have_tRNS=MagickFalse;
10085 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10087 if (logging != MagickFalse)
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " Setting up PLTE chunk with %d colors (%d)",
10090 number_colors, image_colors);
10092 for (i=0; i < (ssize_t) number_colors; i++)
10094 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10095 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10096 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10097 if (logging != MagickFalse)
10098 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099 #if MAGICKCORE_QUANTUM_DEPTH == 8
10100 " %3ld (%3d,%3d,%3d)",
10102 " %5ld (%5d,%5d,%5d)",
10104 (long) i,palette[i].red,palette[i].green,palette[i].blue);
10108 ping_have_PLTE=MagickTrue;
10109 image_depth=ping_bit_depth;
10112 if (matte != MagickFalse)
10115 Identify which colormap entry is transparent.
10117 assert(number_colors <= 256);
10118 assert(image->colormap != NULL);
10120 for (i=0; i < (ssize_t) number_transparent; i++)
10121 ping_trans_alpha[i]=0;
10124 ping_num_trans=(unsigned short) (number_transparent +
10125 number_semitransparent);
10127 if (ping_num_trans == 0)
10128 ping_have_tRNS=MagickFalse;
10131 ping_have_tRNS=MagickTrue;
10134 if (ping_exclude_bKGD == MagickFalse)
10137 * Identify which colormap entry is the background color.
10140 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10141 if (IsPNGColorEqual(ping_background,image->colormap[i]))
10144 ping_background.index=(png_byte) i;
10146 if (logging != MagickFalse)
10148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10149 " background_color index is %d",
10150 (int) ping_background.index);
10153 } /* end of write_png8 */
10155 else if (mng_info->write_png_colortype == 1)
10157 image_matte=MagickFalse;
10158 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10161 else if (mng_info->write_png24 || mng_info->write_png48 ||
10162 mng_info->write_png_colortype == 3)
10164 image_matte=MagickFalse;
10165 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10168 else if (mng_info->write_png32 || mng_info->write_png64 ||
10169 mng_info->write_png_colortype == 7)
10171 image_matte=MagickTrue;
10172 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10175 else /* mng_info->write_pngNN not specified */
10177 image_depth=ping_bit_depth;
10179 if (mng_info->write_png_colortype != 0)
10181 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10183 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10184 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10185 image_matte=MagickTrue;
10188 image_matte=MagickFalse;
10190 if (logging != MagickFalse)
10191 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10192 " PNG colortype %d was specified:",(int) ping_color_type);
10195 else /* write_png_colortype not specified */
10197 if (logging != MagickFalse)
10198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199 " Selecting PNG colortype:");
10201 if (image_info->type == TrueColorType)
10203 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10204 image_matte=MagickFalse;
10206 else if (image_info->type == TrueColorAlphaType)
10208 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10209 image_matte=MagickTrue;
10211 else if (image_info->type == PaletteType ||
10212 image_info->type == PaletteAlphaType)
10213 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10216 if (ping_have_color == MagickFalse)
10218 if (image_matte == MagickFalse)
10220 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10221 image_matte=MagickFalse;
10226 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10227 image_matte=MagickTrue;
10232 if (image_matte == MagickFalse)
10234 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10235 image_matte=MagickFalse;
10240 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10241 image_matte=MagickTrue;
10248 if (logging != MagickFalse)
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " Selected PNG colortype=%d",ping_color_type);
10252 if (ping_bit_depth < 8)
10254 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10255 ping_color_type == PNG_COLOR_TYPE_RGB ||
10256 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10260 old_bit_depth=ping_bit_depth;
10262 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10264 if (image->alpha_trait == UndefinedPixelTrait &&
10265 ping_have_non_bw == MagickFalse)
10269 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10274 if (image->colors == 0)
10277 png_error(ping,"image has 0 colors");
10280 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10281 ping_bit_depth <<= 1;
10284 if (logging != MagickFalse)
10286 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10287 " Number of colors: %.20g",(double) image_colors);
10289 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10290 " Tentative PNG bit depth: %d",ping_bit_depth);
10293 if (ping_bit_depth < (int) mng_info->write_png_depth)
10294 ping_bit_depth = mng_info->write_png_depth;
10297 image_depth=ping_bit_depth;
10299 if (logging != MagickFalse)
10301 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10302 " Tentative PNG color type: %s (%.20g)",
10303 PngColorTypeToString(ping_color_type),
10304 (double) ping_color_type);
10306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10307 " image_info->type: %.20g",(double) image_info->type);
10309 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10310 " image_depth: %.20g",(double) image_depth);
10312 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10314 " image->depth: %.20g",(double) image->depth);
10316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10317 " ping_bit_depth: %.20g",(double) ping_bit_depth);
10320 if (matte != MagickFalse)
10322 if (mng_info->IsPalette)
10324 if (mng_info->write_png_colortype == 0)
10326 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10328 if (ping_have_color != MagickFalse)
10329 ping_color_type=PNG_COLOR_TYPE_RGBA;
10333 * Determine if there is any transparent color.
10335 if (number_transparent + number_semitransparent == 0)
10338 No transparent pixels are present. Change 4 or 6 to 0 or 2.
10341 image_matte=MagickFalse;
10343 if (mng_info->write_png_colortype == 0)
10344 ping_color_type&=0x03;
10354 if (ping_bit_depth == 8)
10357 if (ping_bit_depth == 4)
10360 if (ping_bit_depth == 2)
10363 if (ping_bit_depth == 1)
10366 ping_trans_color.red=(png_uint_16)
10367 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10369 ping_trans_color.green=(png_uint_16)
10370 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10372 ping_trans_color.blue=(png_uint_16)
10373 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10375 ping_trans_color.gray=(png_uint_16)
10376 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10377 image->colormap)) & mask);
10379 ping_trans_color.index=(png_byte) 0;
10381 ping_have_tRNS=MagickTrue;
10384 if (ping_have_tRNS != MagickFalse)
10387 * Determine if there is one and only one transparent color
10388 * and if so if it is fully transparent.
10390 if (ping_have_cheap_transparency == MagickFalse)
10391 ping_have_tRNS=MagickFalse;
10394 if (ping_have_tRNS != MagickFalse)
10396 if (mng_info->write_png_colortype == 0)
10397 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10399 if (image_depth == 8)
10401 ping_trans_color.red&=0xff;
10402 ping_trans_color.green&=0xff;
10403 ping_trans_color.blue&=0xff;
10404 ping_trans_color.gray&=0xff;
10410 if (image_depth == 8)
10412 ping_trans_color.red&=0xff;
10413 ping_trans_color.green&=0xff;
10414 ping_trans_color.blue&=0xff;
10415 ping_trans_color.gray&=0xff;
10422 if (ping_have_tRNS != MagickFalse)
10423 image_matte=MagickFalse;
10425 if ((mng_info->IsPalette) &&
10426 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10427 ping_have_color == MagickFalse &&
10428 (image_matte == MagickFalse || image_depth >= 8))
10432 if (image_matte != MagickFalse)
10433 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10435 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10437 ping_color_type=PNG_COLOR_TYPE_GRAY;
10439 if (save_image_depth == 16 && image_depth == 8)
10441 if (logging != MagickFalse)
10443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10444 " Scaling ping_trans_color (0)");
10446 ping_trans_color.gray*=0x0101;
10450 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10451 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10453 if ((image_colors == 0) ||
10454 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10455 image_colors=(int) (one << image_depth);
10457 if (image_depth > 8)
10463 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10465 if(!mng_info->write_png_depth)
10469 while ((int) (one << ping_bit_depth)
10470 < (ssize_t) image_colors)
10471 ping_bit_depth <<= 1;
10475 else if (ping_color_type ==
10476 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10477 mng_info->IsPalette)
10479 /* Check if grayscale is reducible */
10482 depth_4_ok=MagickTrue,
10483 depth_2_ok=MagickTrue,
10484 depth_1_ok=MagickTrue;
10486 for (i=0; i < (ssize_t) image_colors; i++)
10491 intensity=ScaleQuantumToChar(image->colormap[i].red);
10493 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10494 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10495 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10496 depth_2_ok=depth_1_ok=MagickFalse;
10497 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10498 depth_1_ok=MagickFalse;
10501 if (depth_1_ok && mng_info->write_png_depth <= 1)
10504 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10507 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10512 image_depth=ping_bit_depth;
10517 if (mng_info->IsPalette)
10519 number_colors=image_colors;
10521 if (image_depth <= 8)
10526 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10528 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10530 for (i=0; i < (ssize_t) number_colors; i++)
10532 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10534 ScaleQuantumToChar(image->colormap[i].green);
10535 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10538 if (logging != MagickFalse)
10539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10540 " Setting up PLTE chunk with %d colors",
10543 ping_have_PLTE=MagickTrue;
10546 /* color_type is PNG_COLOR_TYPE_PALETTE */
10547 if (mng_info->write_png_depth == 0)
10555 while ((one << ping_bit_depth) < (size_t) number_colors)
10556 ping_bit_depth <<= 1;
10561 if (matte != MagickFalse)
10564 * Set up trans_colors array.
10566 assert(number_colors <= 256);
10568 ping_num_trans=(unsigned short) (number_transparent +
10569 number_semitransparent);
10571 if (ping_num_trans == 0)
10572 ping_have_tRNS=MagickFalse;
10576 if (logging != MagickFalse)
10578 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10579 " Scaling ping_trans_color (1)");
10581 ping_have_tRNS=MagickTrue;
10583 for (i=0; i < ping_num_trans; i++)
10585 ping_trans_alpha[i]= (png_byte)
10586 ScaleQuantumToChar(image->colormap[i].alpha);
10596 if (image_depth < 8)
10599 if ((save_image_depth == 16) && (image_depth == 8))
10601 if (logging != MagickFalse)
10603 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10604 " Scaling ping_trans_color from (%d,%d,%d)",
10605 (int) ping_trans_color.red,
10606 (int) ping_trans_color.green,
10607 (int) ping_trans_color.blue);
10610 ping_trans_color.red*=0x0101;
10611 ping_trans_color.green*=0x0101;
10612 ping_trans_color.blue*=0x0101;
10613 ping_trans_color.gray*=0x0101;
10615 if (logging != MagickFalse)
10617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10619 (int) ping_trans_color.red,
10620 (int) ping_trans_color.green,
10621 (int) ping_trans_color.blue);
10626 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10627 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10630 Adjust background and transparency samples in sub-8-bit grayscale files.
10632 if (ping_bit_depth < 8 && ping_color_type ==
10633 PNG_COLOR_TYPE_GRAY)
10641 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10643 if (ping_exclude_bKGD == MagickFalse)
10646 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10647 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10648 &image->background_color))) +.5)));
10650 if (logging != MagickFalse)
10651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652 " Setting up bKGD chunk (2)");
10653 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10654 " background_color index is %d",
10655 (int) ping_background.index);
10657 ping_have_bKGD = MagickTrue;
10660 if (logging != MagickFalse)
10661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662 " Scaling ping_trans_color.gray from %d",
10663 (int)ping_trans_color.gray);
10665 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10666 ping_trans_color.gray)+.5);
10668 if (logging != MagickFalse)
10669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10670 " to %d", (int)ping_trans_color.gray);
10673 if (ping_exclude_bKGD == MagickFalse)
10675 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10678 Identify which colormap entry is the background color.
10681 number_colors=image_colors;
10683 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10684 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10687 ping_background.index=(png_byte) i;
10689 if (logging != MagickFalse)
10691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10692 " Setting up bKGD chunk with index=%d",(int) i);
10695 if (i < (ssize_t) number_colors)
10697 ping_have_bKGD = MagickTrue;
10699 if (logging != MagickFalse)
10701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10702 " background =(%d,%d,%d)",
10703 (int) ping_background.red,
10704 (int) ping_background.green,
10705 (int) ping_background.blue);
10709 else /* Can't happen */
10711 if (logging != MagickFalse)
10712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10713 " No room in PLTE to add bKGD color");
10714 ping_have_bKGD = MagickFalse;
10719 if (logging != MagickFalse)
10720 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10724 Initialize compression level and filtering.
10726 if (logging != MagickFalse)
10728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10729 " Setting up deflate compression");
10731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10732 " Compression buffer size: 32768");
10735 png_set_compression_buffer_size(ping,32768L);
10737 if (logging != MagickFalse)
10738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10739 " Compression mem level: 9");
10741 png_set_compression_mem_level(ping, 9);
10743 /* Untangle the "-quality" setting:
10745 Undefined is 0; the default is used.
10750 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10751 zlib default compression level
10753 1-9: the zlib compression level
10757 0-4: the PNG filter method
10759 5: libpng adaptive filtering if compression level > 5
10760 libpng filter type "none" if compression level <= 5
10761 or if image is grayscale or palette
10763 6: libpng adaptive filtering
10765 7: "LOCO" filtering (intrapixel differing) if writing
10766 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10767 and earlier because of a missing "else".
10769 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10770 filtering. Unused prior to IM-6.7.0-10, was same as 6
10772 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10773 Unused prior to IM-6.7.0-10, was same as 6
10775 Note that using the -quality option, not all combinations of
10776 PNG filter type, zlib compression level, and zlib compression
10777 strategy are possible. This will be addressed soon in a
10778 release that accomodates "-define png:compression-strategy", etc.
10782 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10783 image_info->quality;
10787 if (mng_info->write_png_compression_strategy == 0)
10788 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10791 else if (mng_info->write_png_compression_level == 0)
10796 level=(int) MagickMin((ssize_t) quality/10,9);
10798 mng_info->write_png_compression_level = level+1;
10801 if (mng_info->write_png_compression_strategy == 0)
10803 if ((quality %10) == 8 || (quality %10) == 9)
10804 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10805 mng_info->write_png_compression_strategy=Z_RLE+1;
10807 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10811 if (mng_info->write_png_compression_filter == 0)
10812 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10814 if (logging != MagickFalse)
10816 if (mng_info->write_png_compression_level)
10817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10818 " Compression level: %d",
10819 (int) mng_info->write_png_compression_level-1);
10821 if (mng_info->write_png_compression_strategy)
10822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10823 " Compression strategy: %d",
10824 (int) mng_info->write_png_compression_strategy-1);
10826 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10827 " Setting up filtering");
10829 if (mng_info->write_png_compression_filter == 6)
10830 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10831 " Base filter method: ADAPTIVE");
10832 else if (mng_info->write_png_compression_filter == 0 ||
10833 mng_info->write_png_compression_filter == 1)
10834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10835 " Base filter method: NONE");
10837 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10838 " Base filter method: %d",
10839 (int) mng_info->write_png_compression_filter-1);
10842 if (mng_info->write_png_compression_level != 0)
10843 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10845 if (mng_info->write_png_compression_filter == 6)
10847 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10848 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10850 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10852 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10854 else if (mng_info->write_png_compression_filter == 7 ||
10855 mng_info->write_png_compression_filter == 10)
10856 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10858 else if (mng_info->write_png_compression_filter == 8)
10860 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10861 if (mng_info->write_mng)
10863 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10864 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10865 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10868 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10871 else if (mng_info->write_png_compression_filter == 9)
10872 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10874 else if (mng_info->write_png_compression_filter != 0)
10875 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10876 mng_info->write_png_compression_filter-1);
10878 if (mng_info->write_png_compression_strategy != 0)
10879 png_set_compression_strategy(ping,
10880 mng_info->write_png_compression_strategy-1);
10882 ping_interlace_method=image_info->interlace != NoInterlace;
10884 if (mng_info->write_mng)
10885 png_set_sig_bytes(ping,8);
10887 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10889 if (mng_info->write_png_colortype != 0)
10891 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10892 if (ping_have_color != MagickFalse)
10894 ping_color_type = PNG_COLOR_TYPE_RGB;
10896 if (ping_bit_depth < 8)
10900 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10901 if (ping_have_color != MagickFalse)
10902 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10905 if (ping_need_colortype_warning != MagickFalse ||
10906 ((mng_info->write_png_depth &&
10907 (int) mng_info->write_png_depth != ping_bit_depth) ||
10908 (mng_info->write_png_colortype &&
10909 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10910 mng_info->write_png_colortype != 7 &&
10911 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10913 if (logging != MagickFalse)
10915 if (ping_need_colortype_warning != MagickFalse)
10917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10918 " Image has transparency but tRNS chunk was excluded");
10921 if (mng_info->write_png_depth)
10923 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10924 " Defined png:bit-depth=%u, Computed depth=%u",
10925 mng_info->write_png_depth,
10929 if (mng_info->write_png_colortype)
10931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932 " Defined png:color-type=%u, Computed color type=%u",
10933 mng_info->write_png_colortype-1,
10939 "Cannot write image with defined png:bit-depth or png:color-type.");
10942 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10944 /* Add an opaque matte channel */
10945 image->alpha_trait = BlendPixelTrait;
10946 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10948 if (logging != MagickFalse)
10949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10950 " Added an opaque matte channel");
10953 if (number_transparent != 0 || number_semitransparent != 0)
10955 if (ping_color_type < 4)
10957 ping_have_tRNS=MagickTrue;
10958 if (logging != MagickFalse)
10959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10960 " Setting ping_have_tRNS=MagickTrue.");
10964 if (logging != MagickFalse)
10965 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10966 " Writing PNG header chunks");
10968 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10969 ping_bit_depth,ping_color_type,
10970 ping_interlace_method,ping_compression_method,
10971 ping_filter_method);
10973 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10975 png_set_PLTE(ping,ping_info,palette,number_colors);
10977 if (logging != MagickFalse)
10979 for (i=0; i< (ssize_t) number_colors; i++)
10981 if (i < ping_num_trans)
10982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10983 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10985 (int) palette[i].red,
10986 (int) palette[i].green,
10987 (int) palette[i].blue,
10989 (int) ping_trans_alpha[i]);
10991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10992 " PLTE[%d] = (%d,%d,%d)",
10994 (int) palette[i].red,
10995 (int) palette[i].green,
10996 (int) palette[i].blue);
11001 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
11002 if (ping_exclude_sRGB != MagickFalse ||
11003 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11005 if ((ping_exclude_tEXt == MagickFalse ||
11006 ping_exclude_zTXt == MagickFalse) &&
11007 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
11009 ResetImageProfileIterator(image);
11010 for (name=GetNextImageProfile(image); name != (char *) NULL; )
11012 profile=GetImageProfile(image,name);
11014 if (profile != (StringInfo *) NULL)
11016 #ifdef PNG_WRITE_iCCP_SUPPORTED
11017 if ((LocaleCompare(name,"ICC") == 0) ||
11018 (LocaleCompare(name,"ICM") == 0))
11020 ping_have_iCCP = MagickTrue;
11021 if (ping_exclude_iCCP == MagickFalse)
11023 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11024 " Setting up iCCP chunk");
11026 png_set_iCCP(ping,ping_info,(png_charp) name,0,
11027 #if (PNG_LIBPNG_VER < 10500)
11028 (png_charp) GetStringInfoDatum(profile),
11030 (const png_byte *) GetStringInfoDatum(profile),
11032 (png_uint_32) GetStringInfoLength(profile));
11036 /* Do not write hex-encoded ICC chunk */
11037 name=GetNextImageProfile(image);
11041 #endif /* WRITE_iCCP */
11043 if (LocaleCompare(name,"exif") == 0)
11045 /* Do not write hex-encoded ICC chunk; we will
11046 write it later as an eXIf chunk */
11047 name=GetNextImageProfile(image);
11051 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11052 " Setting up zTXt chunk with uuencoded %s profile",
11054 Magick_png_write_raw_profile(image_info,ping,ping_info,
11055 (unsigned char *) name,(unsigned char *) name,
11056 GetStringInfoDatum(profile),
11057 (png_uint_32) GetStringInfoLength(profile));
11059 name=GetNextImageProfile(image);
11064 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11065 if ((mng_info->have_write_global_srgb == 0) &&
11066 ping_have_iCCP != MagickTrue &&
11067 (ping_have_sRGB != MagickFalse ||
11068 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11070 if (ping_exclude_sRGB == MagickFalse)
11073 Note image rendering intent.
11075 if (logging != MagickFalse)
11076 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11077 " Setting up sRGB chunk");
11079 (void) png_set_sRGB(ping,ping_info,(
11080 Magick_RenderingIntent_to_PNG_RenderingIntent(
11081 image->rendering_intent)));
11083 ping_have_sRGB = MagickTrue;
11087 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11090 if (ping_exclude_gAMA == MagickFalse &&
11091 ping_have_iCCP == MagickFalse &&
11092 ping_have_sRGB == MagickFalse &&
11093 (ping_exclude_sRGB == MagickFalse ||
11094 (image->gamma < .45 || image->gamma > .46)))
11096 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11100 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11102 if (logging != MagickFalse)
11103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11104 " Setting up gAMA chunk");
11106 png_set_gAMA(ping,ping_info,image->gamma);
11110 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11112 if ((mng_info->have_write_global_chrm == 0) &&
11113 (image->chromaticity.red_primary.x != 0.0))
11116 Note image chromaticity.
11117 Note: if cHRM+gAMA == sRGB write sRGB instead.
11125 wp=image->chromaticity.white_point;
11126 rp=image->chromaticity.red_primary;
11127 gp=image->chromaticity.green_primary;
11128 bp=image->chromaticity.blue_primary;
11130 if (logging != MagickFalse)
11131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11132 " Setting up cHRM chunk");
11134 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11140 if (ping_exclude_bKGD == MagickFalse)
11142 if (ping_have_bKGD != MagickFalse)
11144 png_set_bKGD(ping,ping_info,&ping_background);
11145 if (logging != MagickFalse)
11147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11148 " Setting up bKGD chunk");
11149 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11150 " background color = (%d,%d,%d)",
11151 (int) ping_background.red,
11152 (int) ping_background.green,
11153 (int) ping_background.blue);
11154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11155 " index = %d, gray=%d",
11156 (int) ping_background.index,
11157 (int) ping_background.gray);
11162 if (ping_exclude_pHYs == MagickFalse)
11164 if (ping_have_pHYs != MagickFalse)
11166 png_set_pHYs(ping,ping_info,
11167 ping_pHYs_x_resolution,
11168 ping_pHYs_y_resolution,
11169 ping_pHYs_unit_type);
11171 if (logging != MagickFalse)
11173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174 " Setting up pHYs chunk");
11175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11176 " x_resolution=%lu",
11177 (unsigned long) ping_pHYs_x_resolution);
11178 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11179 " y_resolution=%lu",
11180 (unsigned long) ping_pHYs_y_resolution);
11181 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11183 (unsigned long) ping_pHYs_unit_type);
11188 #if defined(PNG_tIME_SUPPORTED)
11189 if (ping_exclude_tIME == MagickFalse)
11194 if (image->taint == MagickFalse)
11196 timestamp=GetImageOption(image_info,"png:tIME");
11198 if (timestamp == (const char *) NULL)
11199 timestamp=GetImageProperty(image,"png:tIME",exception);
11204 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11205 " Reset tIME in tainted image");
11207 timestamp=GetImageProperty(image,"date:modify",exception);
11210 if (timestamp != (const char *) NULL)
11211 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11215 if (mng_info->need_blob != MagickFalse)
11217 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11219 png_error(ping,"WriteBlob Failed");
11221 ping_have_blob=MagickTrue;
11224 png_write_info_before_PLTE(ping, ping_info);
11226 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11228 if (logging != MagickFalse)
11230 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11231 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11234 if (ping_color_type == 3)
11235 (void) png_set_tRNS(ping, ping_info,
11242 (void) png_set_tRNS(ping, ping_info,
11245 &ping_trans_color);
11247 if (logging != MagickFalse)
11249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11250 " tRNS color =(%d,%d,%d)",
11251 (int) ping_trans_color.red,
11252 (int) ping_trans_color.green,
11253 (int) ping_trans_color.blue);
11258 png_write_info(ping,ping_info);
11260 /* write orNT if image->orientation is defined */
11261 if (image->orientation != UndefinedOrientation)
11265 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
11266 PNGType(chunk,mng_orNT);
11267 LogPNGChunk(logging,mng_orNT,1L);
11268 /* PNG uses Exif orientation values */
11269 chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11270 (void) WriteBlob(image,5,chunk);
11271 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11274 ping_wrote_caNv = MagickFalse;
11276 /* write caNv chunk */
11277 if (ping_exclude_caNv == MagickFalse)
11279 if ((image->page.width != 0 && image->page.width != image->columns) ||
11280 (image->page.height != 0 && image->page.height != image->rows) ||
11281 image->page.x != 0 || image->page.y != 0)
11286 (void) WriteBlobMSBULong(image,16L); /* data length=8 */
11287 PNGType(chunk,mng_caNv);
11288 LogPNGChunk(logging,mng_caNv,16L);
11289 PNGLong(chunk+4,(png_uint_32) image->page.width);
11290 PNGLong(chunk+8,(png_uint_32) image->page.height);
11291 PNGsLong(chunk+12,(png_int_32) image->page.x);
11292 PNGsLong(chunk+16,(png_int_32) image->page.y);
11293 (void) WriteBlob(image,20,chunk);
11294 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11295 ping_wrote_caNv = MagickTrue;
11299 #if defined(PNG_oFFs_SUPPORTED)
11300 if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11302 if (image->page.x || image->page.y)
11304 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11305 (png_int_32) image->page.y, 0);
11307 if (logging != MagickFalse)
11308 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11309 " Setting up oFFs chunk with x=%d, y=%d, units=0",
11310 (int) image->page.x, (int) image->page.y);
11315 #if (PNG_LIBPNG_VER == 10206)
11316 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11317 #define PNG_HAVE_IDAT 0x04
11318 ping->mode |= PNG_HAVE_IDAT;
11319 #undef PNG_HAVE_IDAT
11322 png_set_packing(ping);
11326 rowbytes=image->columns;
11327 if (image_depth > 8)
11329 switch (ping_color_type)
11331 case PNG_COLOR_TYPE_RGB:
11335 case PNG_COLOR_TYPE_GRAY_ALPHA:
11339 case PNG_COLOR_TYPE_RGBA:
11347 if (logging != MagickFalse)
11349 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11350 " Writing PNG image data");
11352 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11353 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11355 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11356 if (pixel_info == (MemoryInfo *) NULL)
11357 png_error(ping,"Allocation of memory for pixels failed");
11358 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11360 Initialize image scanlines.
11362 quantum_info=AcquireQuantumInfo(image_info,image);
11363 if (quantum_info == (QuantumInfo *) NULL)
11364 png_error(ping,"Memory allocation for quantum_info failed");
11365 quantum_info->format=UndefinedQuantumFormat;
11366 SetQuantumDepth(image,quantum_info,image_depth);
11367 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11368 num_passes=png_set_interlace_handling(ping);
11370 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11371 !mng_info->write_png48 && !mng_info->write_png64 &&
11372 !mng_info->write_png32) &&
11373 (mng_info->IsPalette ||
11374 (image_info->type == BilevelType)) &&
11375 image_matte == MagickFalse &&
11376 ping_have_non_bw == MagickFalse)
11378 /* Palette, Bilevel, or Opaque Monochrome */
11379 register const Quantum
11382 SetQuantumDepth(image,quantum_info,8);
11383 for (pass=0; pass < num_passes; pass++)
11386 Convert PseudoClass image to a PNG monochrome image.
11388 for (y=0; y < (ssize_t) image->rows; y++)
11390 if (logging != MagickFalse && y == 0)
11391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11392 " Writing row of pixels (0)");
11394 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11396 if (p == (const Quantum *) NULL)
11399 if (mng_info->IsPalette)
11401 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11402 quantum_info,GrayQuantum,ping_pixels,exception);
11403 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11404 mng_info->write_png_depth &&
11405 mng_info->write_png_depth != old_bit_depth)
11407 /* Undo pixel scaling */
11408 for (i=0; i < (ssize_t) image->columns; i++)
11409 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11410 >> (8-old_bit_depth));
11416 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11417 quantum_info,RedQuantum,ping_pixels,exception);
11420 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11421 for (i=0; i < (ssize_t) image->columns; i++)
11422 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11425 if (logging != MagickFalse && y == 0)
11426 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11427 " Writing row of pixels (1)");
11429 png_write_row(ping,ping_pixels);
11431 status=SetImageProgress(image,SaveImageTag,
11432 (MagickOffsetType) (pass * image->rows + y),
11433 num_passes * image->rows);
11435 if (status == MagickFalse)
11441 else /* Not Palette, Bilevel, or Opaque Monochrome */
11443 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11444 !mng_info->write_png48 && !mng_info->write_png64 &&
11445 !mng_info->write_png32) && (image_matte != MagickFalse ||
11446 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11447 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11449 register const Quantum
11452 for (pass=0; pass < num_passes; pass++)
11455 for (y=0; y < (ssize_t) image->rows; y++)
11457 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11459 if (p == (const Quantum *) NULL)
11462 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11464 if (mng_info->IsPalette)
11465 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11466 quantum_info,GrayQuantum,ping_pixels,exception);
11469 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11470 quantum_info,RedQuantum,ping_pixels,exception);
11472 if (logging != MagickFalse && y == 0)
11473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11474 " Writing GRAY PNG pixels (2)");
11477 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11479 if (logging != MagickFalse && y == 0)
11480 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11481 " Writing GRAY_ALPHA PNG pixels (2)");
11483 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11484 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11487 if (logging != MagickFalse && y == 0)
11488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11489 " Writing row of pixels (2)");
11491 png_write_row(ping,ping_pixels);
11493 status=SetImageProgress(image,SaveImageTag,
11494 (MagickOffsetType) (pass * image->rows + y),
11495 num_passes * image->rows);
11497 if (status == MagickFalse)
11505 register const Quantum
11508 for (pass=0; pass < num_passes; pass++)
11510 if ((image_depth > 8) ||
11511 mng_info->write_png24 ||
11512 mng_info->write_png32 ||
11513 mng_info->write_png48 ||
11514 mng_info->write_png64 ||
11515 (!mng_info->write_png8 && !mng_info->IsPalette))
11517 for (y=0; y < (ssize_t) image->rows; y++)
11519 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11521 if (p == (const Quantum *) NULL)
11524 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11526 if (image->storage_class == DirectClass)
11527 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11528 quantum_info,RedQuantum,ping_pixels,exception);
11531 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11532 quantum_info,GrayQuantum,ping_pixels,exception);
11535 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11537 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11538 quantum_info,GrayAlphaQuantum,ping_pixels,
11541 if (logging != MagickFalse && y == 0)
11542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11543 " Writing GRAY_ALPHA PNG pixels (3)");
11546 else if (image_matte != MagickFalse)
11547 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11548 quantum_info,RGBAQuantum,ping_pixels,exception);
11551 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11552 quantum_info,RGBQuantum,ping_pixels,exception);
11554 if (logging != MagickFalse && y == 0)
11555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11556 " Writing row of pixels (3)");
11558 png_write_row(ping,ping_pixels);
11560 status=SetImageProgress(image,SaveImageTag,
11561 (MagickOffsetType) (pass * image->rows + y),
11562 num_passes * image->rows);
11564 if (status == MagickFalse)
11570 /* not ((image_depth > 8) ||
11571 mng_info->write_png24 || mng_info->write_png32 ||
11572 mng_info->write_png48 || mng_info->write_png64 ||
11573 (!mng_info->write_png8 && !mng_info->IsPalette))
11576 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11577 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11579 if (logging != MagickFalse)
11580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11581 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11583 SetQuantumDepth(image,quantum_info,8);
11587 for (y=0; y < (ssize_t) image->rows; y++)
11589 if (logging != MagickFalse && y == 0)
11590 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11591 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11594 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11596 if (p == (const Quantum *) NULL)
11599 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11601 SetQuantumDepth(image,quantum_info,image->depth);
11603 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11604 quantum_info,GrayQuantum,ping_pixels,exception);
11607 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11609 if (logging != MagickFalse && y == 0)
11610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11611 " Writing GRAY_ALPHA PNG pixels (4)");
11613 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11614 quantum_info,GrayAlphaQuantum,ping_pixels,
11620 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11621 quantum_info,IndexQuantum,ping_pixels,exception);
11623 if (logging != MagickFalse && y <= 2)
11625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11626 " Writing row of non-gray pixels (4)");
11628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11629 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11630 (int)ping_pixels[0],(int)ping_pixels[1]);
11633 png_write_row(ping,ping_pixels);
11635 status=SetImageProgress(image,SaveImageTag,
11636 (MagickOffsetType) (pass * image->rows + y),
11637 num_passes * image->rows);
11639 if (status == MagickFalse)
11647 if (quantum_info != (QuantumInfo *) NULL)
11648 quantum_info=DestroyQuantumInfo(quantum_info);
11650 if (logging != MagickFalse)
11652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11653 " Wrote PNG image data");
11655 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11656 " Width: %.20g",(double) ping_width);
11658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11659 " Height: %.20g",(double) ping_height);
11661 if (mng_info->write_png_depth)
11663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11664 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11668 " PNG bit-depth written: %d",ping_bit_depth);
11670 if (mng_info->write_png_colortype)
11672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11673 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11677 " PNG color-type written: %d",ping_color_type);
11679 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11680 " PNG Interlace method: %d",ping_interlace_method);
11683 Generate text chunks after IDAT.
11685 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11687 ResetImagePropertyIterator(image);
11688 property=GetNextImageProperty(image);
11689 while (property != (const char *) NULL)
11694 value=GetImageProperty(image,property,exception);
11696 /* Don't write any "png:" or "jpeg:" properties; those are just for
11697 * "identify" or for passing through to another JPEG
11699 if ((LocaleNCompare(property,"png:",4) != 0 &&
11700 LocaleNCompare(property,"jpeg:",5) != 0) &&
11703 /* Suppress density and units if we wrote a pHYs chunk */
11704 (ping_exclude_pHYs != MagickFalse ||
11705 LocaleCompare(property,"density") != 0 ||
11706 LocaleCompare(property,"units") != 0) &&
11708 /* Suppress the IM-generated Date:create and Date:modify */
11709 (ping_exclude_date == MagickFalse ||
11710 LocaleNCompare(property, "Date:",5) != 0))
11712 if (value != (const char *) NULL)
11715 #if PNG_LIBPNG_VER >= 10400
11716 text=(png_textp) png_malloc(ping,
11717 (png_alloc_size_t) sizeof(png_text));
11719 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11721 text[0].key=(char *) property;
11722 text[0].text=(char *) value;
11723 text[0].text_length=strlen(value);
11725 if (ping_exclude_tEXt != MagickFalse)
11726 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11728 else if (ping_exclude_zTXt != MagickFalse)
11729 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11733 text[0].compression=image_info->compression == NoCompression ||
11734 (image_info->compression == UndefinedCompression &&
11735 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11736 PNG_TEXT_COMPRESSION_zTXt ;
11739 if (logging != MagickFalse)
11741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11742 " Setting up text chunk");
11744 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11745 " keyword: '%s'",text[0].key);
11748 png_set_text(ping,ping_info,text,1);
11749 png_free(ping,text);
11752 property=GetNextImageProperty(image);
11756 /* write eXIf profile */
11757 if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11759 ResetImageProfileIterator(image);
11761 for (name=GetNextImageProfile(image); name != (char *) NULL; )
11763 if (LocaleCompare(name,"exif") == 0)
11765 profile=GetImageProfile(image,name);
11767 if (profile != (StringInfo *) NULL)
11779 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11780 " Have eXIf profile");
11782 ping_profile=CloneStringInfo(profile);
11783 data=GetStringInfoDatum(ping_profile),
11784 length=(png_uint_32) GetStringInfoLength(ping_profile);
11786 PNGType(chunk,mng_eXIf);
11789 ping_profile=DestroyStringInfo(ping_profile);
11790 break; /* otherwise crashes */
11793 if (*data == 'E' && *(data+1) == 'x' && *(data+2) == 'i' &&
11794 *(data+3) == 'f' && *(data+4) == '\0' && *(data+5) == '\0')
11796 /* skip the "Exif\0\0" JFIF Exif Header ID */
11801 LogPNGChunk(logging,chunk,length);
11802 (void) WriteBlobMSBULong(image,length);
11803 (void) WriteBlob(image,4,chunk);
11804 (void) WriteBlob(image,length,data);
11805 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4), data,
11807 ping_profile=DestroyStringInfo(ping_profile);
11811 name=GetNextImageProfile(image);
11815 if (logging != MagickFalse)
11816 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11817 " Writing PNG end info");
11819 png_write_end(ping,ping_info);
11821 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11823 if (mng_info->page.x || mng_info->page.y ||
11824 (ping_width != mng_info->page.width) ||
11825 (ping_height != mng_info->page.height))
11831 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11833 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11834 PNGType(chunk,mng_FRAM);
11835 LogPNGChunk(logging,mng_FRAM,27L);
11837 chunk[5]=0; /* frame name separator (no name) */
11838 chunk[6]=1; /* flag for changing delay, for next frame only */
11839 chunk[7]=0; /* flag for changing frame timeout */
11840 chunk[8]=1; /* flag for changing frame clipping for next frame */
11841 chunk[9]=0; /* flag for changing frame sync_id */
11842 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11843 chunk[14]=0; /* clipping boundaries delta type */
11844 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11846 (png_uint_32) (mng_info->page.x + ping_width));
11847 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11849 (png_uint_32) (mng_info->page.y + ping_height));
11850 (void) WriteBlob(image,31,chunk);
11851 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11852 mng_info->old_framing_mode=4;
11853 mng_info->framing_mode=1;
11857 mng_info->framing_mode=3;
11859 if (mng_info->write_mng && !mng_info->need_fram &&
11860 ((int) image->dispose == 3))
11861 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11864 Free PNG resources.
11867 png_destroy_write_struct(&ping,&ping_info);
11869 pixel_info=RelinquishVirtualMemory(pixel_info);
11871 if (ping_have_blob != MagickFalse)
11872 (void) CloseBlob(image);
11874 image_info=DestroyImageInfo(image_info);
11875 image=DestroyImage(image);
11877 /* Store bit depth actually written */
11878 s[0]=(char) ping_bit_depth;
11881 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11883 if (logging != MagickFalse)
11884 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11885 " exit WriteOnePNGImage()");
11887 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11888 UnlockSemaphoreInfo(ping_semaphore);
11891 /* } for navigation to beginning of SETJMP-protected block. Revert to
11892 * Throwing an Exception when an error occurs.
11895 return(MagickTrue);
11896 /* End write one PNG image */
11901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11905 % W r i t e P N G I m a g e %
11909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11911 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11912 % Multiple-image Network Graphics (MNG) image file.
11914 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11916 % The format of the WritePNGImage method is:
11918 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11919 % Image *image,ExceptionInfo *exception)
11921 % A description of each parameter follows:
11923 % o image_info: the image info.
11925 % o image: The image.
11927 % o exception: return any errors or warnings in this structure.
11929 % Returns MagickTrue on success, MagickFalse on failure.
11931 % Communicating with the PNG encoder:
11933 % While the datastream written is always in PNG format and normally would
11934 % be given the "png" file extension, this method also writes the following
11935 % pseudo-formats which are subsets of png:
11937 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11938 % a depth greater than 8, the depth is reduced. If transparency
11939 % is present, the tRNS chunk must only have values 0 and 255
11940 % (i.e., transparency is binary: fully opaque or fully
11941 % transparent). If other values are present they will be
11942 % 50%-thresholded to binary transparency. If more than 256
11943 % colors are present, they will be quantized to the 4-4-4-1,
11944 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11945 % of any resulting fully-transparent pixels is changed to
11946 % the image's background color.
11948 % If you want better quantization or dithering of the colors
11949 % or alpha than that, you need to do it before calling the
11950 % PNG encoder. The pixels contain 8-bit indices even if
11951 % they could be represented with 1, 2, or 4 bits. Grayscale
11952 % images will be written as indexed PNG files even though the
11953 % PNG grayscale type might be slightly more efficient. Please
11954 % note that writing to the PNG8 format may result in loss
11955 % of color and alpha data.
11957 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11958 % chunk can be present to convey binary transparency by naming
11959 % one of the colors as transparent. The only loss incurred
11960 % is reduction of sample depth to 8. If the image has more
11961 % than one transparent color, has semitransparent pixels, or
11962 % has an opaque pixel with the same RGB components as the
11963 % transparent color, an image is not written.
11965 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11966 % transparency is permitted, i.e., the alpha sample for
11967 % each pixel can have any value from 0 to 255. The alpha
11968 % channel is present even if the image is fully opaque.
11969 % The only loss in data is the reduction of the sample depth
11972 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11973 % chunk can be present to convey binary transparency by naming
11974 % one of the colors as transparent. If the image has more
11975 % than one transparent color, has semitransparent pixels, or
11976 % has an opaque pixel with the same RGB components as the
11977 % transparent color, an image is not written.
11979 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11980 % transparency is permitted, i.e., the alpha sample for
11981 % each pixel can have any value from 0 to 65535. The alpha
11982 % channel is present even if the image is fully opaque.
11984 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11985 % image, if the input was a PNG, is written. If these values
11986 % cannot be found, or if the pixels have been changed in a way
11987 % that makes this impossible, then "PNG00" falls back to the
11988 % regular "PNG" format.
11990 % o -define: For more precise control of the PNG output, you can use the
11991 % Image options "png:bit-depth" and "png:color-type". These
11992 % can be set from the commandline with "-define" and also
11993 % from the application programming interfaces. The options
11994 % are case-independent and are converted to lowercase before
11995 % being passed to this encoder.
11997 % png:color-type can be 0, 2, 3, 4, or 6.
11999 % When png:color-type is 0 (Grayscale), png:bit-depth can
12000 % be 1, 2, 4, 8, or 16.
12002 % When png:color-type is 2 (RGB), png:bit-depth can
12005 % When png:color-type is 3 (Indexed), png:bit-depth can
12006 % be 1, 2, 4, or 8. This refers to the number of bits
12007 % used to store the index. The color samples always have
12008 % bit-depth 8 in indexed PNG files.
12010 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12011 % png:bit-depth can be 8 or 16.
12013 % If the image cannot be written without loss with the
12014 % requested bit-depth and color-type, a PNG file will not
12015 % be written, a warning will be issued, and the encoder will
12016 % return MagickFalse.
12018 % Since image encoders should not be responsible for the "heavy lifting",
12019 % the user should make sure that ImageMagick has already reduced the
12020 % image depth and number of colors and limit transparency to binary
12021 % transparency prior to attempting to write the image with depth, color,
12022 % or transparency limitations.
12024 % Note that another definition, "png:bit-depth-written" exists, but it
12025 % is not intended for external use. It is only used internally by the
12026 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12028 % As of version 6.6.6 the following optimizations are always done:
12030 % o 32-bit depth is reduced to 16.
12031 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
12032 % high byte and low byte are identical.
12033 % o Palette is sorted to remove unused entries and to put a
12034 % transparent color first, if BUILD_PNG_PALETTE is defined.
12035 % o Opaque matte channel is removed when writing an indexed PNG.
12036 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
12037 % this can be done without loss and a larger bit depth N was not
12038 % requested via the "-define png:bit-depth=N" option.
12039 % o If matte channel is present but only one transparent color is
12040 % present, RGB+tRNS is written instead of RGBA
12041 % o Opaque matte channel is removed (or added, if color-type 4 or 6
12042 % was requested when converting an opaque image).
12044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12046 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12047 Image *image,ExceptionInfo *exception)
12066 assert(image_info != (const ImageInfo *) NULL);
12067 assert(image_info->signature == MagickCoreSignature);
12068 assert(image != (Image *) NULL);
12069 assert(image->signature == MagickCoreSignature);
12070 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12071 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12073 Allocate a MngInfo structure.
12075 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12077 if (mng_info == (MngInfo *) NULL)
12078 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12081 Initialize members of the MngInfo structure.
12083 (void) memset(mng_info,0,sizeof(MngInfo));
12084 mng_info->image=image;
12085 mng_info->equal_backgrounds=MagickTrue;
12087 /* See if user has requested a specific PNG subformat */
12089 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12090 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12091 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12092 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12093 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12095 value=GetImageOption(image_info,"png:format");
12097 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12100 " Format=%s",value);
12102 mng_info->write_png8 = MagickFalse;
12103 mng_info->write_png24 = MagickFalse;
12104 mng_info->write_png32 = MagickFalse;
12105 mng_info->write_png48 = MagickFalse;
12106 mng_info->write_png64 = MagickFalse;
12108 if (LocaleCompare(value,"png8") == 0)
12109 mng_info->write_png8 = MagickTrue;
12111 else if (LocaleCompare(value,"png24") == 0)
12112 mng_info->write_png24 = MagickTrue;
12114 else if (LocaleCompare(value,"png32") == 0)
12115 mng_info->write_png32 = MagickTrue;
12117 else if (LocaleCompare(value,"png48") == 0)
12118 mng_info->write_png48 = MagickTrue;
12120 else if (LocaleCompare(value,"png64") == 0)
12121 mng_info->write_png64 = MagickTrue;
12123 else if ((LocaleCompare(value,"png00") == 0) ||
12124 LocaleCompare(image_info->magick,"PNG00") == 0)
12126 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12127 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12129 if (value != (char *) NULL)
12131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12132 " png00 inherited bit depth=%s",value);
12134 if (LocaleCompare(value,"1") == 0)
12135 mng_info->write_png_depth = 1;
12137 else if (LocaleCompare(value,"2") == 0)
12138 mng_info->write_png_depth = 2;
12140 else if (LocaleCompare(value,"4") == 0)
12141 mng_info->write_png_depth = 4;
12143 else if (LocaleCompare(value,"8") == 0)
12144 mng_info->write_png_depth = 8;
12146 else if (LocaleCompare(value,"16") == 0)
12147 mng_info->write_png_depth = 16;
12150 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12152 if (value != (char *) NULL)
12154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12155 " png00 inherited color type=%s",value);
12157 if (LocaleCompare(value,"0") == 0)
12158 mng_info->write_png_colortype = 1;
12160 else if (LocaleCompare(value,"2") == 0)
12161 mng_info->write_png_colortype = 3;
12163 else if (LocaleCompare(value,"3") == 0)
12164 mng_info->write_png_colortype = 4;
12166 else if (LocaleCompare(value,"4") == 0)
12167 mng_info->write_png_colortype = 5;
12169 else if (LocaleCompare(value,"6") == 0)
12170 mng_info->write_png_colortype = 7;
12175 if (mng_info->write_png8)
12177 mng_info->write_png_colortype = /* 3 */ 4;
12178 mng_info->write_png_depth = 8;
12182 if (mng_info->write_png24)
12184 mng_info->write_png_colortype = /* 2 */ 3;
12185 mng_info->write_png_depth = 8;
12188 if (image->alpha_trait != UndefinedPixelTrait)
12189 (void) SetImageType(image,TrueColorAlphaType,exception);
12192 (void) SetImageType(image,TrueColorType,exception);
12194 (void) SyncImage(image,exception);
12197 if (mng_info->write_png32)
12199 mng_info->write_png_colortype = /* 6 */ 7;
12200 mng_info->write_png_depth = 8;
12202 image->alpha_trait = BlendPixelTrait;
12204 (void) SetImageType(image,TrueColorAlphaType,exception);
12205 (void) SyncImage(image,exception);
12208 if (mng_info->write_png48)
12210 mng_info->write_png_colortype = /* 2 */ 3;
12211 mng_info->write_png_depth = 16;
12214 if (image->alpha_trait != UndefinedPixelTrait)
12215 (void) SetImageType(image,TrueColorAlphaType,exception);
12218 (void) SetImageType(image,TrueColorType,exception);
12220 (void) SyncImage(image,exception);
12223 if (mng_info->write_png64)
12225 mng_info->write_png_colortype = /* 6 */ 7;
12226 mng_info->write_png_depth = 16;
12228 image->alpha_trait = BlendPixelTrait;
12230 (void) SetImageType(image,TrueColorAlphaType,exception);
12231 (void) SyncImage(image,exception);
12234 value=GetImageOption(image_info,"png:bit-depth");
12236 if (value != (char *) NULL)
12238 if (LocaleCompare(value,"1") == 0)
12239 mng_info->write_png_depth = 1;
12241 else if (LocaleCompare(value,"2") == 0)
12242 mng_info->write_png_depth = 2;
12244 else if (LocaleCompare(value,"4") == 0)
12245 mng_info->write_png_depth = 4;
12247 else if (LocaleCompare(value,"8") == 0)
12248 mng_info->write_png_depth = 8;
12250 else if (LocaleCompare(value,"16") == 0)
12251 mng_info->write_png_depth = 16;
12254 (void) ThrowMagickException(exception,
12255 GetMagickModule(),CoderWarning,
12256 "ignoring invalid defined png:bit-depth",
12259 if (logging != MagickFalse)
12260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12261 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12264 value=GetImageOption(image_info,"png:color-type");
12266 if (value != (char *) NULL)
12268 /* We must store colortype+1 because 0 is a valid colortype */
12269 if (LocaleCompare(value,"0") == 0)
12270 mng_info->write_png_colortype = 1;
12272 else if (LocaleCompare(value,"1") == 0)
12273 mng_info->write_png_colortype = 2;
12275 else if (LocaleCompare(value,"2") == 0)
12276 mng_info->write_png_colortype = 3;
12278 else if (LocaleCompare(value,"3") == 0)
12279 mng_info->write_png_colortype = 4;
12281 else if (LocaleCompare(value,"4") == 0)
12282 mng_info->write_png_colortype = 5;
12284 else if (LocaleCompare(value,"6") == 0)
12285 mng_info->write_png_colortype = 7;
12288 (void) ThrowMagickException(exception,
12289 GetMagickModule(),CoderWarning,
12290 "ignoring invalid defined png:color-type",
12293 if (logging != MagickFalse)
12294 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12295 " png:color-type=%d was defined.\n",
12296 mng_info->write_png_colortype-1);
12299 /* Check for chunks to be excluded:
12301 * The default is to not exclude any known chunks except for any
12302 * listed in the "unused_chunks" array, above.
12304 * Chunks can be listed for exclusion via a "png:exclude-chunk"
12305 * define (in the image properties or in the image artifacts)
12306 * or via a mng_info member. For convenience, in addition
12307 * to or instead of a comma-separated list of chunks, the
12308 * "exclude-chunk" string can be simply "all" or "none".
12310 * Note that the "-strip" option provides a convenient way of
12311 * doing the equivalent of
12313 * -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12314 * iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12316 * The exclude-chunk define takes priority over the mng_info.
12318 * A "png:include-chunk" define takes priority over both the
12319 * mng_info and the "png:exclude-chunk" define. Like the
12320 * "exclude-chunk" string, it can define "all" or "none" as
12321 * well as a comma-separated list. Chunks that are unknown to
12322 * ImageMagick are always excluded, regardless of their "copy-safe"
12323 * status according to the PNG specification, and even if they
12324 * appear in the "include-chunk" list. Such defines appearing among
12325 * the image options take priority over those found among the image
12328 * Finally, all chunks listed in the "unused_chunks" array are
12329 * automatically excluded, regardless of the other instructions
12332 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12333 * will not be written and the gAMA chunk will only be written if it
12334 * is not between .45 and .46, or approximately (1.0/2.2).
12336 * If you exclude tRNS and the image has transparency, the colortype
12337 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12339 * The -strip option causes StripImage() to set the png:include-chunk
12340 * artifact to "none,trns,gama".
12343 mng_info->ping_exclude_bKGD=MagickFalse;
12344 mng_info->ping_exclude_caNv=MagickFalse;
12345 mng_info->ping_exclude_cHRM=MagickFalse;
12346 mng_info->ping_exclude_date=MagickFalse;
12347 mng_info->ping_exclude_eXIf=MagickFalse;
12348 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12349 mng_info->ping_exclude_gAMA=MagickFalse;
12350 mng_info->ping_exclude_iCCP=MagickFalse;
12351 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12352 mng_info->ping_exclude_oFFs=MagickFalse;
12353 mng_info->ping_exclude_pHYs=MagickFalse;
12354 mng_info->ping_exclude_sRGB=MagickFalse;
12355 mng_info->ping_exclude_tEXt=MagickFalse;
12356 mng_info->ping_exclude_tIME=MagickFalse;
12357 mng_info->ping_exclude_tRNS=MagickFalse;
12358 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12359 mng_info->ping_exclude_zTXt=MagickFalse;
12361 mng_info->ping_preserve_colormap=MagickFalse;
12363 value=GetImageOption(image_info,"png:preserve-colormap");
12365 value=GetImageArtifact(image,"png:preserve-colormap");
12367 mng_info->ping_preserve_colormap=MagickTrue;
12369 mng_info->ping_preserve_iCCP=MagickFalse;
12371 value=GetImageOption(image_info,"png:preserve-iCCP");
12373 value=GetImageArtifact(image,"png:preserve-iCCP");
12375 mng_info->ping_preserve_iCCP=MagickTrue;
12377 /* These compression-level, compression-strategy, and compression-filter
12378 * defines take precedence over values from the -quality option.
12380 value=GetImageOption(image_info,"png:compression-level");
12382 value=GetImageArtifact(image,"png:compression-level");
12385 /* We have to add 1 to everything because 0 is a valid input,
12386 * and we want to use 0 (the default) to mean undefined.
12388 if (LocaleCompare(value,"0") == 0)
12389 mng_info->write_png_compression_level = 1;
12391 else if (LocaleCompare(value,"1") == 0)
12392 mng_info->write_png_compression_level = 2;
12394 else if (LocaleCompare(value,"2") == 0)
12395 mng_info->write_png_compression_level = 3;
12397 else if (LocaleCompare(value,"3") == 0)
12398 mng_info->write_png_compression_level = 4;
12400 else if (LocaleCompare(value,"4") == 0)
12401 mng_info->write_png_compression_level = 5;
12403 else if (LocaleCompare(value,"5") == 0)
12404 mng_info->write_png_compression_level = 6;
12406 else if (LocaleCompare(value,"6") == 0)
12407 mng_info->write_png_compression_level = 7;
12409 else if (LocaleCompare(value,"7") == 0)
12410 mng_info->write_png_compression_level = 8;
12412 else if (LocaleCompare(value,"8") == 0)
12413 mng_info->write_png_compression_level = 9;
12415 else if (LocaleCompare(value,"9") == 0)
12416 mng_info->write_png_compression_level = 10;
12419 (void) ThrowMagickException(exception,
12420 GetMagickModule(),CoderWarning,
12421 "ignoring invalid defined png:compression-level",
12425 value=GetImageOption(image_info,"png:compression-strategy");
12427 value=GetImageArtifact(image,"png:compression-strategy");
12430 if (LocaleCompare(value,"0") == 0)
12431 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12433 else if (LocaleCompare(value,"1") == 0)
12434 mng_info->write_png_compression_strategy = Z_FILTERED+1;
12436 else if (LocaleCompare(value,"2") == 0)
12437 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12439 else if (LocaleCompare(value,"3") == 0)
12440 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
12441 mng_info->write_png_compression_strategy = Z_RLE+1;
12443 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12446 else if (LocaleCompare(value,"4") == 0)
12447 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
12448 mng_info->write_png_compression_strategy = Z_FIXED+1;
12450 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12454 (void) ThrowMagickException(exception,
12455 GetMagickModule(),CoderWarning,
12456 "ignoring invalid defined png:compression-strategy",
12460 value=GetImageOption(image_info,"png:compression-filter");
12462 value=GetImageArtifact(image,"png:compression-filter");
12465 /* To do: combinations of filters allowed by libpng
12466 * masks 0x08 through 0xf8
12468 * Implement this as a comma-separated list of 0,1,2,3,4,5
12469 * where 5 is a special case meaning PNG_ALL_FILTERS.
12472 if (LocaleCompare(value,"0") == 0)
12473 mng_info->write_png_compression_filter = 1;
12475 else if (LocaleCompare(value,"1") == 0)
12476 mng_info->write_png_compression_filter = 2;
12478 else if (LocaleCompare(value,"2") == 0)
12479 mng_info->write_png_compression_filter = 3;
12481 else if (LocaleCompare(value,"3") == 0)
12482 mng_info->write_png_compression_filter = 4;
12484 else if (LocaleCompare(value,"4") == 0)
12485 mng_info->write_png_compression_filter = 5;
12487 else if (LocaleCompare(value,"5") == 0)
12488 mng_info->write_png_compression_filter = 6;
12491 (void) ThrowMagickException(exception,
12492 GetMagickModule(),CoderWarning,
12493 "ignoring invalid defined png:compression-filter",
12497 for (source=0; source<8; source++)
12502 value=GetImageOption(image_info,"png:exclude-chunks");
12505 value=GetImageArtifact(image,"png:exclude-chunks");
12508 value=GetImageOption(image_info,"png:exclude-chunk");
12511 value=GetImageArtifact(image,"png:exclude-chunk");
12514 value=GetImageOption(image_info,"png:include-chunks");
12517 value=GetImageArtifact(image,"png:include-chunks");
12520 value=GetImageOption(image_info,"png:include-chunk");
12523 value=GetImageArtifact(image,"png:include-chunk");
12529 excluding = MagickTrue;
12531 excluding = MagickFalse;
12533 if (logging != MagickFalse)
12535 if (source == 0 || source == 2)
12536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12537 " png:exclude-chunk=%s found in image options.\n", value);
12538 else if (source == 1 || source == 3)
12539 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12540 " png:exclude-chunk=%s found in image artifacts.\n", value);
12541 else if (source == 4 || source == 6)
12542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12543 " png:include-chunk=%s found in image options.\n", value);
12544 else /* if (source == 5 || source == 7) */
12545 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12546 " png:include-chunk=%s found in image artifacts.\n", value);
12549 if (IsOptionMember("all",value) != MagickFalse)
12551 mng_info->ping_exclude_bKGD=excluding;
12552 mng_info->ping_exclude_caNv=excluding;
12553 mng_info->ping_exclude_cHRM=excluding;
12554 mng_info->ping_exclude_date=excluding;
12555 mng_info->ping_exclude_EXIF=excluding;
12556 mng_info->ping_exclude_eXIf=excluding;
12557 mng_info->ping_exclude_gAMA=excluding;
12558 mng_info->ping_exclude_iCCP=excluding;
12559 /* mng_info->ping_exclude_iTXt=excluding; */
12560 mng_info->ping_exclude_oFFs=excluding;
12561 mng_info->ping_exclude_pHYs=excluding;
12562 mng_info->ping_exclude_sRGB=excluding;
12563 mng_info->ping_exclude_tEXt=excluding;
12564 mng_info->ping_exclude_tIME=excluding;
12565 mng_info->ping_exclude_tRNS=excluding;
12566 mng_info->ping_exclude_zCCP=excluding;
12567 mng_info->ping_exclude_zTXt=excluding;
12570 if (IsOptionMember("none",value) != MagickFalse)
12572 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12574 mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12576 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12578 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12580 mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12582 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12584 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12586 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12588 /* mng_info->ping_exclude_iTXt=!excluding; */
12589 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12591 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12593 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12595 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12597 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12599 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12601 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12603 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12607 if (IsOptionMember("bkgd",value) != MagickFalse)
12608 mng_info->ping_exclude_bKGD=excluding;
12610 if (IsOptionMember("caNv",value) != MagickFalse)
12611 mng_info->ping_exclude_caNv=excluding;
12613 if (IsOptionMember("chrm",value) != MagickFalse)
12614 mng_info->ping_exclude_cHRM=excluding;
12616 if (IsOptionMember("date",value) != MagickFalse)
12617 mng_info->ping_exclude_date=excluding;
12619 if (IsOptionMember("exif",value) != MagickFalse)
12621 mng_info->ping_exclude_EXIF=excluding;
12622 mng_info->ping_exclude_eXIf=excluding;
12625 if (IsOptionMember("gama",value) != MagickFalse)
12626 mng_info->ping_exclude_gAMA=excluding;
12628 if (IsOptionMember("iccp",value) != MagickFalse)
12629 mng_info->ping_exclude_iCCP=excluding;
12632 if (IsOptionMember("itxt",value) != MagickFalse)
12633 mng_info->ping_exclude_iTXt=excluding;
12636 if (IsOptionMember("offs",value) != MagickFalse)
12637 mng_info->ping_exclude_oFFs=excluding;
12639 if (IsOptionMember("phys",value) != MagickFalse)
12640 mng_info->ping_exclude_pHYs=excluding;
12642 if (IsOptionMember("srgb",value) != MagickFalse)
12643 mng_info->ping_exclude_sRGB=excluding;
12645 if (IsOptionMember("text",value) != MagickFalse)
12646 mng_info->ping_exclude_tEXt=excluding;
12648 if (IsOptionMember("time",value) != MagickFalse)
12649 mng_info->ping_exclude_tIME=excluding;
12651 if (IsOptionMember("trns",value) != MagickFalse)
12652 mng_info->ping_exclude_tRNS=excluding;
12654 if (IsOptionMember("zccp",value) != MagickFalse)
12655 mng_info->ping_exclude_zCCP=excluding;
12657 if (IsOptionMember("ztxt",value) != MagickFalse)
12658 mng_info->ping_exclude_zTXt=excluding;
12661 if (logging != MagickFalse)
12663 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12664 " Chunks to be excluded from the output png:");
12665 if (mng_info->ping_exclude_bKGD != MagickFalse)
12666 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12668 if (mng_info->ping_exclude_caNv != MagickFalse)
12669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12671 if (mng_info->ping_exclude_cHRM != MagickFalse)
12672 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12674 if (mng_info->ping_exclude_date != MagickFalse)
12675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12677 if (mng_info->ping_exclude_EXIF != MagickFalse)
12678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12680 if (mng_info->ping_exclude_eXIf != MagickFalse)
12681 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12683 if (mng_info->ping_exclude_gAMA != MagickFalse)
12684 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686 if (mng_info->ping_exclude_iCCP != MagickFalse)
12687 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12690 if (mng_info->ping_exclude_iTXt != MagickFalse)
12691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12695 if (mng_info->ping_exclude_oFFs != MagickFalse)
12696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12698 if (mng_info->ping_exclude_pHYs != MagickFalse)
12699 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12701 if (mng_info->ping_exclude_sRGB != MagickFalse)
12702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12704 if (mng_info->ping_exclude_tEXt != MagickFalse)
12705 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12707 if (mng_info->ping_exclude_tIME != MagickFalse)
12708 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12710 if (mng_info->ping_exclude_tRNS != MagickFalse)
12711 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12713 if (mng_info->ping_exclude_zCCP != MagickFalse)
12714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716 if (mng_info->ping_exclude_zTXt != MagickFalse)
12717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12721 mng_info->need_blob = MagickTrue;
12723 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12725 mng_info=MngInfoFreeStruct(mng_info);
12727 if (logging != MagickFalse)
12728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12733 #if defined(JNG_SUPPORTED)
12735 /* Write one JNG image */
12736 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12737 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12758 jng_alpha_compression_method,
12759 jng_alpha_sample_depth,
12767 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12768 " Enter WriteOneJNGImage()");
12770 blob=(unsigned char *) NULL;
12771 jpeg_image=(Image *) NULL;
12772 jpeg_image_info=(ImageInfo *) NULL;
12776 transparent=image_info->type==GrayscaleAlphaType ||
12777 image_info->type==TrueColorAlphaType ||
12778 image->alpha_trait != UndefinedPixelTrait;
12780 jng_alpha_sample_depth = 0;
12782 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12784 jng_alpha_compression_method=(image->compression==JPEGCompression ||
12785 image_info->compression==JPEGCompression) ? 8 : 0;
12787 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12788 image_info->quality;
12790 if (jng_alpha_quality >= 1000)
12791 jng_alpha_quality /= 1000;
12795 if (transparent != 0)
12799 /* Create JPEG blob, image, and image_info */
12800 if (logging != MagickFalse)
12801 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12802 " Creating jpeg_image_info for alpha.");
12804 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12806 if (jpeg_image_info == (ImageInfo *) NULL)
12808 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12809 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12812 if (logging != MagickFalse)
12813 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12814 " Creating jpeg_image.");
12816 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12817 if (jpeg_image == (Image *) NULL)
12818 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12819 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12820 jpeg_image->alpha_trait=UndefinedPixelTrait;
12821 jpeg_image->quality=jng_alpha_quality;
12822 jpeg_image_info->type=GrayscaleType;
12823 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12824 (void) AcquireUniqueFilename(jpeg_image->filename);
12825 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12826 "%s",jpeg_image->filename);
12830 jng_alpha_compression_method=0;
12832 jng_alpha_sample_depth=0;
12835 /* To do: check bit depth of PNG alpha channel */
12837 /* Check if image is grayscale. */
12838 if (image_info->type != TrueColorAlphaType && image_info->type !=
12839 TrueColorType && SetImageGray(image,exception))
12842 if (logging != MagickFalse)
12844 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12845 " JNG Quality = %d",(int) jng_quality);
12846 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12847 " JNG Color Type = %d",jng_color_type);
12848 if (transparent != 0)
12850 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12851 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12853 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12854 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12855 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12859 if (transparent != 0)
12861 if (jng_alpha_compression_method==0)
12866 /* Encode alpha as a grayscale PNG blob */
12867 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12869 if (status == MagickFalse)
12870 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12872 if (logging != MagickFalse)
12873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12874 " Creating PNG blob.");
12876 (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12878 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12879 jpeg_image_info->interlace=NoInterlace;
12881 /* Exclude all ancillary chunks */
12882 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12884 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12885 &length,exception);
12887 /* Retrieve sample depth used */
12888 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12889 if (value != (char *) NULL)
12890 jng_alpha_sample_depth= (unsigned int) value[0];
12894 /* Encode alpha as a grayscale JPEG blob */
12896 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12898 if (status == MagickFalse)
12899 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12901 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12903 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12904 jpeg_image_info->interlace=NoInterlace;
12905 if (logging != MagickFalse)
12906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12907 " Creating blob.");
12908 blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12909 jpeg_image,&length,
12911 jng_alpha_sample_depth=8;
12913 if (logging != MagickFalse)
12914 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12915 " Successfully read jpeg_image into a blob, length=%.20g.",
12919 /* Destroy JPEG image and image_info */
12920 jpeg_image=DestroyImage(jpeg_image);
12921 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12922 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12925 /* Write JHDR chunk */
12926 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12927 PNGType(chunk,mng_JHDR);
12928 LogPNGChunk(logging,mng_JHDR,16L);
12929 PNGLong(chunk+4,(png_uint_32) image->columns);
12930 PNGLong(chunk+8,(png_uint_32) image->rows);
12931 chunk[12]=jng_color_type;
12932 chunk[13]=8; /* sample depth */
12933 chunk[14]=8; /*jng_image_compression_method */
12934 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12935 chunk[16]=jng_alpha_sample_depth;
12936 chunk[17]=jng_alpha_compression_method;
12937 chunk[18]=0; /*jng_alpha_filter_method */
12938 chunk[19]=0; /*jng_alpha_interlace_method */
12939 (void) WriteBlob(image,20,chunk);
12940 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12941 if (logging != MagickFalse)
12943 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12944 " JNG width:%15lu",(unsigned long) image->columns);
12946 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12947 " JNG height:%14lu",(unsigned long) image->rows);
12949 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12950 " JNG color type:%10d",jng_color_type);
12952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12953 " JNG sample depth:%8d",8);
12955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12956 " JNG compression:%9d",8);
12958 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12959 " JNG interlace:%11d",0);
12961 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12962 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12965 " JNG alpha compression:%3d",jng_alpha_compression_method);
12967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12968 " JNG alpha filter:%8d",0);
12970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12971 " JNG alpha interlace:%5d",0);
12975 Write leading ancillary chunks
12978 if (transparent != 0)
12981 Write JNG bKGD chunk
12992 if (jng_color_type == 8 || jng_color_type == 12)
12996 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12997 PNGType(chunk,mng_bKGD);
12998 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12999 red=ScaleQuantumToChar(image->background_color.red);
13000 green=ScaleQuantumToChar(image->background_color.green);
13001 blue=ScaleQuantumToChar(image->background_color.blue);
13008 (void) WriteBlob(image,(size_t) num_bytes,chunk);
13009 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13012 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13015 Write JNG sRGB chunk
13017 (void) WriteBlobMSBULong(image,1L);
13018 PNGType(chunk,mng_sRGB);
13019 LogPNGChunk(logging,mng_sRGB,1L);
13021 if (image->rendering_intent != UndefinedIntent)
13022 chunk[4]=(unsigned char)
13023 Magick_RenderingIntent_to_PNG_RenderingIntent(
13024 (image->rendering_intent));
13027 chunk[4]=(unsigned char)
13028 Magick_RenderingIntent_to_PNG_RenderingIntent(
13029 (PerceptualIntent));
13031 (void) WriteBlob(image,5,chunk);
13032 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13036 if (image->gamma != 0.0)
13039 Write JNG gAMA chunk
13041 (void) WriteBlobMSBULong(image,4L);
13042 PNGType(chunk,mng_gAMA);
13043 LogPNGChunk(logging,mng_gAMA,4L);
13044 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13045 (void) WriteBlob(image,8,chunk);
13046 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13049 if ((mng_info->equal_chrms == MagickFalse) &&
13050 (image->chromaticity.red_primary.x != 0.0))
13056 Write JNG cHRM chunk
13058 (void) WriteBlobMSBULong(image,32L);
13059 PNGType(chunk,mng_cHRM);
13060 LogPNGChunk(logging,mng_cHRM,32L);
13061 primary=image->chromaticity.white_point;
13062 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13063 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13064 primary=image->chromaticity.red_primary;
13065 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13066 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13067 primary=image->chromaticity.green_primary;
13068 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13069 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13070 primary=image->chromaticity.blue_primary;
13071 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13072 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13073 (void) WriteBlob(image,36,chunk);
13074 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13078 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13081 Write JNG pHYs chunk
13083 (void) WriteBlobMSBULong(image,9L);
13084 PNGType(chunk,mng_pHYs);
13085 LogPNGChunk(logging,mng_pHYs,9L);
13086 if (image->units == PixelsPerInchResolution)
13088 PNGLong(chunk+4,(png_uint_32)
13089 (image->resolution.x*100.0/2.54+0.5));
13091 PNGLong(chunk+8,(png_uint_32)
13092 (image->resolution.y*100.0/2.54+0.5));
13099 if (image->units == PixelsPerCentimeterResolution)
13101 PNGLong(chunk+4,(png_uint_32)
13102 (image->resolution.x*100.0+0.5));
13104 PNGLong(chunk+8,(png_uint_32)
13105 (image->resolution.y*100.0+0.5));
13112 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13113 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13117 (void) WriteBlob(image,13,chunk);
13118 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13121 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13124 Write JNG oFFs chunk
13126 (void) WriteBlobMSBULong(image,9L);
13127 PNGType(chunk,mng_oFFs);
13128 LogPNGChunk(logging,mng_oFFs,9L);
13129 PNGsLong(chunk+4,(ssize_t) (image->page.x));
13130 PNGsLong(chunk+8,(ssize_t) (image->page.y));
13132 (void) WriteBlob(image,13,chunk);
13133 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13136 if (transparent != 0)
13138 if (jng_alpha_compression_method==0)
13146 /* Write IDAT chunk header */
13147 if (logging != MagickFalse)
13148 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13149 " Write IDAT chunks from blob, length=%.20g.",(double)
13152 /* Copy IDAT chunks */
13155 for (i=8; i<(ssize_t) length; i+=len+12)
13157 len=(((unsigned int) *(p ) & 0xff) << 24) +
13158 (((unsigned int) *(p + 1) & 0xff) << 16) +
13159 (((unsigned int) *(p + 2) & 0xff) << 8) +
13160 (((unsigned int) *(p + 3) & 0xff) ) ;
13163 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13165 /* Found an IDAT chunk. */
13166 (void) WriteBlobMSBULong(image,len);
13167 LogPNGChunk(logging,mng_IDAT,len);
13168 (void) WriteBlob(image,len+4,p);
13169 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13174 if (logging != MagickFalse)
13175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13176 " Skipping %c%c%c%c chunk, length=%.20g.",
13177 *(p),*(p+1),*(p+2),*(p+3),(double) len);
13182 else if (length != 0)
13184 /* Write JDAA chunk header */
13185 if (logging != MagickFalse)
13186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13187 " Write JDAA chunk, length=%.20g.",(double) length);
13188 (void) WriteBlobMSBULong(image,(size_t) length);
13189 PNGType(chunk,mng_JDAA);
13190 LogPNGChunk(logging,mng_JDAA,length);
13191 /* Write JDAT chunk(s) data */
13192 (void) WriteBlob(image,4,chunk);
13193 (void) WriteBlob(image,length,blob);
13194 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13197 blob=(unsigned char *) RelinquishMagickMemory(blob);
13200 /* Encode image as a JPEG blob */
13201 if (logging != MagickFalse)
13202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13203 " Creating jpeg_image_info.");
13204 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13205 if (jpeg_image_info == (ImageInfo *) NULL)
13206 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13208 if (logging != MagickFalse)
13209 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13210 " Creating jpeg_image.");
13212 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13213 if (jpeg_image == (Image *) NULL)
13215 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13216 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13218 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13220 (void) AcquireUniqueFilename(jpeg_image->filename);
13221 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13222 jpeg_image->filename);
13224 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13227 if (logging != MagickFalse)
13228 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13229 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13230 (double) jpeg_image->rows);
13232 if (status == MagickFalse)
13233 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13235 if (jng_color_type == 8 || jng_color_type == 12)
13236 jpeg_image_info->type=GrayscaleType;
13238 jpeg_image_info->quality=jng_quality;
13239 jpeg_image->quality=jng_quality;
13240 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13241 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13243 if (logging != MagickFalse)
13244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13245 " Creating blob.");
13247 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13250 if (logging != MagickFalse)
13252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13253 " Successfully read jpeg_image into a blob, length=%.20g.",
13256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13257 " Write JDAT chunk, length=%.20g.",(double) length);
13260 /* Write JDAT chunk(s) */
13261 (void) WriteBlobMSBULong(image,(size_t) length);
13262 PNGType(chunk,mng_JDAT);
13263 LogPNGChunk(logging,mng_JDAT,length);
13264 (void) WriteBlob(image,4,chunk);
13265 (void) WriteBlob(image,length,blob);
13266 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13268 jpeg_image=DestroyImage(jpeg_image);
13269 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13270 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13271 blob=(unsigned char *) RelinquishMagickMemory(blob);
13273 /* Write IEND chunk */
13274 (void) WriteBlobMSBULong(image,0L);
13275 PNGType(chunk,mng_IEND);
13276 LogPNGChunk(logging,mng_IEND,0);
13277 (void) WriteBlob(image,4,chunk);
13278 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13280 if (logging != MagickFalse)
13281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13282 " exit WriteOneJNGImage()");
13288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13292 % W r i t e J N G I m a g e %
13296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13298 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13300 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
13302 % The format of the WriteJNGImage method is:
13304 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13305 % Image *image,ExceptionInfo *exception)
13307 % A description of each parameter follows:
13309 % o image_info: the image info.
13311 % o image: The image.
13313 % o exception: return any errors or warnings in this structure.
13315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13317 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13318 Image *image, ExceptionInfo *exception)
13330 assert(image_info != (const ImageInfo *) NULL);
13331 assert(image_info->signature == MagickCoreSignature);
13332 assert(image != (Image *) NULL);
13333 assert(image->signature == MagickCoreSignature);
13334 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13335 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13336 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13337 if (status == MagickFalse)
13339 if ((image->columns > 65535UL) || (image->rows > 65535UL))
13340 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13343 Allocate a MngInfo structure.
13345 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13346 if (mng_info == (MngInfo *) NULL)
13347 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13349 Initialize members of the MngInfo structure.
13351 (void) memset(mng_info,0,sizeof(MngInfo));
13352 mng_info->image=image;
13354 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13356 status=WriteOneJNGImage(mng_info,image_info,image,exception);
13357 mng_info=MngInfoFreeStruct(mng_info);
13358 (void) CloseBlob(image);
13360 (void) CatchImageException(image);
13361 if (logging != MagickFalse)
13362 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13367 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13368 Image *image, ExceptionInfo *exception)
13376 volatile MagickBooleanType
13388 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13389 defined(PNG_MNG_FEATURES_SUPPORTED)
13392 all_images_are_gray,
13402 volatile unsigned int
13414 #if (PNG_LIBPNG_VER < 10200)
13415 if (image_info->verbose)
13416 printf("Your PNG library (libpng-%s) is rather old.\n",
13417 PNG_LIBPNG_VER_STRING);
13423 assert(image_info != (const ImageInfo *) NULL);
13424 assert(image_info->signature == MagickCoreSignature);
13425 assert(image != (Image *) NULL);
13426 assert(image->signature == MagickCoreSignature);
13427 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13428 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13429 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13430 if (status == MagickFalse)
13434 Allocate a MngInfo structure.
13436 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13437 if (mng_info == (MngInfo *) NULL)
13438 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13440 Initialize members of the MngInfo structure.
13442 (void) memset(mng_info,0,sizeof(MngInfo));
13443 mng_info->image=image;
13444 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13447 * See if user has requested a specific PNG subformat to be used
13448 * for all of the PNGs in the MNG being written, e.g.,
13450 * convert *.png png8:animation.mng
13452 * To do: check -define png:bit_depth and png:color_type as well,
13453 * or perhaps use mng:bit_depth and mng:color_type instead for
13457 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13458 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13459 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13461 write_jng=MagickFalse;
13462 if (image_info->compression == JPEGCompression)
13463 write_jng=MagickTrue;
13465 mng_info->adjoin=image_info->adjoin &&
13466 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13468 if (logging != MagickFalse)
13470 /* Log some info about the input */
13474 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13475 " Checking input image(s)\n"
13476 " Image_info depth: %.20g, Type: %d",
13477 (double) image_info->depth, image_info->type);
13480 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13484 " Scene: %.20g\n, Image depth: %.20g",
13485 (double) scene++, (double) p->depth);
13487 if (p->alpha_trait != UndefinedPixelTrait)
13488 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13495 if (p->storage_class == PseudoClass)
13496 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13497 " Storage class: PseudoClass");
13500 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13501 " Storage class: DirectClass");
13504 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13505 " Number of colors: %.20g",(double) p->colors);
13508 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13509 " Number of colors: unspecified");
13511 if (mng_info->adjoin == MagickFalse)
13516 use_global_plte=MagickFalse;
13517 all_images_are_gray=MagickFalse;
13518 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13519 need_local_plte=MagickTrue;
13521 need_defi=MagickFalse;
13522 need_matte=MagickFalse;
13523 mng_info->framing_mode=1;
13524 mng_info->old_framing_mode=1;
13527 if (image_info->page != (char *) NULL)
13530 Determine image bounding box.
13532 SetGeometry(image,&mng_info->page);
13533 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13534 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13549 mng_info->page=image->page;
13550 need_geom=MagickTrue;
13551 if (mng_info->page.width || mng_info->page.height)
13552 need_geom=MagickFalse;
13554 Check all the scenes.
13556 initial_delay=image->delay;
13557 need_iterations=MagickFalse;
13558 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13559 mng_info->equal_physs=MagickTrue,
13560 mng_info->equal_gammas=MagickTrue;
13561 mng_info->equal_srgbs=MagickTrue;
13562 mng_info->equal_backgrounds=MagickTrue;
13564 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13565 defined(PNG_MNG_FEATURES_SUPPORTED)
13566 all_images_are_gray=MagickTrue;
13567 mng_info->equal_palettes=MagickFalse;
13568 need_local_plte=MagickFalse;
13570 for (next_image=image; next_image != (Image *) NULL; )
13574 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13575 mng_info->page.width=next_image->columns+next_image->page.x;
13577 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13578 mng_info->page.height=next_image->rows+next_image->page.y;
13581 if (next_image->page.x || next_image->page.y)
13582 need_defi=MagickTrue;
13584 if (next_image->alpha_trait != UndefinedPixelTrait)
13585 need_matte=MagickTrue;
13587 if ((int) next_image->dispose >= BackgroundDispose)
13588 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13589 next_image->page.x || next_image->page.y ||
13590 ((next_image->columns < mng_info->page.width) &&
13591 (next_image->rows < mng_info->page.height)))
13592 mng_info->need_fram=MagickTrue;
13594 if (next_image->iterations)
13595 need_iterations=MagickTrue;
13597 final_delay=next_image->delay;
13599 if (final_delay != initial_delay || final_delay > 1UL*
13600 next_image->ticks_per_second)
13601 mng_info->need_fram=1;
13603 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13604 defined(PNG_MNG_FEATURES_SUPPORTED)
13606 check for global palette possibility.
13608 if (image->alpha_trait != UndefinedPixelTrait)
13609 need_local_plte=MagickTrue;
13611 if (need_local_plte == 0)
13613 if (SetImageGray(image,exception) == MagickFalse)
13614 all_images_are_gray=MagickFalse;
13615 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13616 if (use_global_plte == 0)
13617 use_global_plte=mng_info->equal_palettes;
13618 need_local_plte=!mng_info->equal_palettes;
13621 if (GetNextImageInList(next_image) != (Image *) NULL)
13623 if (next_image->background_color.red !=
13624 next_image->next->background_color.red ||
13625 next_image->background_color.green !=
13626 next_image->next->background_color.green ||
13627 next_image->background_color.blue !=
13628 next_image->next->background_color.blue)
13629 mng_info->equal_backgrounds=MagickFalse;
13631 if (next_image->gamma != next_image->next->gamma)
13632 mng_info->equal_gammas=MagickFalse;
13634 if (next_image->rendering_intent !=
13635 next_image->next->rendering_intent)
13636 mng_info->equal_srgbs=MagickFalse;
13638 if ((next_image->units != next_image->next->units) ||
13639 (next_image->resolution.x != next_image->next->resolution.x) ||
13640 (next_image->resolution.y != next_image->next->resolution.y))
13641 mng_info->equal_physs=MagickFalse;
13643 if (mng_info->equal_chrms)
13645 if (next_image->chromaticity.red_primary.x !=
13646 next_image->next->chromaticity.red_primary.x ||
13647 next_image->chromaticity.red_primary.y !=
13648 next_image->next->chromaticity.red_primary.y ||
13649 next_image->chromaticity.green_primary.x !=
13650 next_image->next->chromaticity.green_primary.x ||
13651 next_image->chromaticity.green_primary.y !=
13652 next_image->next->chromaticity.green_primary.y ||
13653 next_image->chromaticity.blue_primary.x !=
13654 next_image->next->chromaticity.blue_primary.x ||
13655 next_image->chromaticity.blue_primary.y !=
13656 next_image->next->chromaticity.blue_primary.y ||
13657 next_image->chromaticity.white_point.x !=
13658 next_image->next->chromaticity.white_point.x ||
13659 next_image->chromaticity.white_point.y !=
13660 next_image->next->chromaticity.white_point.y)
13661 mng_info->equal_chrms=MagickFalse;
13665 next_image=GetNextImageInList(next_image);
13667 if (image_count < 2)
13669 mng_info->equal_backgrounds=MagickFalse;
13670 mng_info->equal_chrms=MagickFalse;
13671 mng_info->equal_gammas=MagickFalse;
13672 mng_info->equal_srgbs=MagickFalse;
13673 mng_info->equal_physs=MagickFalse;
13674 use_global_plte=MagickFalse;
13675 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13676 need_local_plte=MagickTrue;
13678 need_iterations=MagickFalse;
13681 if (mng_info->need_fram == MagickFalse)
13684 Only certain framing rates 100/n are exactly representable without
13685 the FRAM chunk but we'll allow some slop in VLC files
13687 if (final_delay == 0)
13689 if (need_iterations != MagickFalse)
13692 It's probably a GIF with loop; don't run it *too* fast.
13694 if (mng_info->adjoin)
13697 (void) ThrowMagickException(exception,GetMagickModule(),
13699 "input has zero delay between all frames; assuming",
13704 mng_info->ticks_per_second=0;
13706 if (final_delay != 0)
13707 mng_info->ticks_per_second=(png_uint_32)
13708 (image->ticks_per_second/final_delay);
13709 if (final_delay > 50)
13710 mng_info->ticks_per_second=2;
13712 if (final_delay > 75)
13713 mng_info->ticks_per_second=1;
13715 if (final_delay > 125)
13716 mng_info->need_fram=MagickTrue;
13718 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13719 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13720 (final_delay != 25) && (final_delay != 50) &&
13721 (final_delay != (size_t) image->ticks_per_second))
13722 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13725 if (mng_info->need_fram != MagickFalse)
13726 mng_info->ticks_per_second=image->ticks_per_second;
13728 If pseudocolor, we should also check to see if all the
13729 palettes are identical and write a global PLTE if they are.
13733 Write the MNG version 1.0 signature and MHDR chunk.
13735 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13736 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13737 PNGType(chunk,mng_MHDR);
13738 LogPNGChunk(logging,mng_MHDR,28L);
13739 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13740 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13741 PNGLong(chunk+12,mng_info->ticks_per_second);
13742 PNGLong(chunk+16,0L); /* layer count=unknown */
13743 PNGLong(chunk+20,0L); /* frame count=unknown */
13744 PNGLong(chunk+24,0L); /* play time=unknown */
13749 if (need_defi || mng_info->need_fram || use_global_plte)
13750 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13753 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13758 if (need_defi || mng_info->need_fram || use_global_plte)
13759 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13762 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13770 if (need_defi || mng_info->need_fram || use_global_plte)
13771 PNGLong(chunk+28,11L); /* simplicity=LC */
13774 PNGLong(chunk+28,9L); /* simplicity=VLC */
13779 if (need_defi || mng_info->need_fram || use_global_plte)
13780 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13783 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13786 (void) WriteBlob(image,32,chunk);
13787 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13788 option=GetImageOption(image_info,"mng:need-cacheoff");
13789 if (option != (const char *) NULL)
13794 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13796 PNGType(chunk,mng_nEED);
13797 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13798 (void) WriteBlobMSBULong(image,(size_t) length);
13799 LogPNGChunk(logging,mng_nEED,(size_t) length);
13801 (void) WriteBlob(image,length,chunk);
13802 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13804 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13805 (GetNextImageInList(image) != (Image *) NULL) &&
13806 (image->iterations != 1))
13809 Write MNG TERM chunk
13811 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13812 PNGType(chunk,mng_TERM);
13813 LogPNGChunk(logging,mng_TERM,10L);
13814 chunk[4]=3; /* repeat animation */
13815 chunk[5]=0; /* show last frame when done */
13816 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13817 final_delay/MagickMax(image->ticks_per_second,1)));
13819 if (image->iterations == 0)
13820 PNGLong(chunk+10,PNG_UINT_31_MAX);
13823 PNGLong(chunk+10,(png_uint_32) image->iterations);
13825 if (logging != MagickFalse)
13827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13828 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13829 final_delay/MagickMax(image->ticks_per_second,1)));
13831 if (image->iterations == 0)
13832 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13833 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13836 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13837 " Image iterations: %.20g",(double) image->iterations);
13839 (void) WriteBlob(image,14,chunk);
13840 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13843 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13845 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13846 mng_info->equal_srgbs)
13849 Write MNG sRGB chunk
13851 (void) WriteBlobMSBULong(image,1L);
13852 PNGType(chunk,mng_sRGB);
13853 LogPNGChunk(logging,mng_sRGB,1L);
13855 if (image->rendering_intent != UndefinedIntent)
13856 chunk[4]=(unsigned char)
13857 Magick_RenderingIntent_to_PNG_RenderingIntent(
13858 (image->rendering_intent));
13861 chunk[4]=(unsigned char)
13862 Magick_RenderingIntent_to_PNG_RenderingIntent(
13863 (PerceptualIntent));
13865 (void) WriteBlob(image,5,chunk);
13866 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13867 mng_info->have_write_global_srgb=MagickTrue;
13872 if (image->gamma && mng_info->equal_gammas)
13875 Write MNG gAMA chunk
13877 (void) WriteBlobMSBULong(image,4L);
13878 PNGType(chunk,mng_gAMA);
13879 LogPNGChunk(logging,mng_gAMA,4L);
13880 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13881 (void) WriteBlob(image,8,chunk);
13882 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13883 mng_info->have_write_global_gama=MagickTrue;
13885 if (mng_info->equal_chrms)
13891 Write MNG cHRM chunk
13893 (void) WriteBlobMSBULong(image,32L);
13894 PNGType(chunk,mng_cHRM);
13895 LogPNGChunk(logging,mng_cHRM,32L);
13896 primary=image->chromaticity.white_point;
13897 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13898 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13899 primary=image->chromaticity.red_primary;
13900 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13901 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13902 primary=image->chromaticity.green_primary;
13903 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13904 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13905 primary=image->chromaticity.blue_primary;
13906 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13907 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13908 (void) WriteBlob(image,36,chunk);
13909 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13910 mng_info->have_write_global_chrm=MagickTrue;
13913 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13916 Write MNG pHYs chunk
13918 (void) WriteBlobMSBULong(image,9L);
13919 PNGType(chunk,mng_pHYs);
13920 LogPNGChunk(logging,mng_pHYs,9L);
13922 if (image->units == PixelsPerInchResolution)
13924 PNGLong(chunk+4,(png_uint_32)
13925 (image->resolution.x*100.0/2.54+0.5));
13927 PNGLong(chunk+8,(png_uint_32)
13928 (image->resolution.y*100.0/2.54+0.5));
13935 if (image->units == PixelsPerCentimeterResolution)
13937 PNGLong(chunk+4,(png_uint_32)
13938 (image->resolution.x*100.0+0.5));
13940 PNGLong(chunk+8,(png_uint_32)
13941 (image->resolution.y*100.0+0.5));
13948 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13949 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13953 (void) WriteBlob(image,13,chunk);
13954 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13957 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13958 or does not cover the entire frame.
13960 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13961 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13962 (image->page.width+image->page.x < mng_info->page.width))
13963 || (image->page.height && (image->page.height+image->page.y
13964 < mng_info->page.height))))
13966 (void) WriteBlobMSBULong(image,6L);
13967 PNGType(chunk,mng_BACK);
13968 LogPNGChunk(logging,mng_BACK,6L);
13969 red=ScaleQuantumToShort(image->background_color.red);
13970 green=ScaleQuantumToShort(image->background_color.green);
13971 blue=ScaleQuantumToShort(image->background_color.blue);
13972 PNGShort(chunk+4,red);
13973 PNGShort(chunk+6,green);
13974 PNGShort(chunk+8,blue);
13975 (void) WriteBlob(image,10,chunk);
13976 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13977 if (mng_info->equal_backgrounds)
13979 (void) WriteBlobMSBULong(image,6L);
13980 PNGType(chunk,mng_bKGD);
13981 LogPNGChunk(logging,mng_bKGD,6L);
13982 (void) WriteBlob(image,10,chunk);
13983 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13987 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13988 if ((need_local_plte == MagickFalse) &&
13989 (image->storage_class == PseudoClass) &&
13990 (all_images_are_gray == MagickFalse))
13996 Write MNG PLTE chunk
13998 data_length=3*image->colors;
13999 (void) WriteBlobMSBULong(image,data_length);
14000 PNGType(chunk,mng_PLTE);
14001 LogPNGChunk(logging,mng_PLTE,data_length);
14003 for (i=0; i < (ssize_t) image->colors; i++)
14005 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14006 image->colormap[i].red) & 0xff);
14007 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14008 image->colormap[i].green) & 0xff);
14009 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14010 image->colormap[i].blue) & 0xff);
14013 (void) WriteBlob(image,data_length+4,chunk);
14014 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14015 mng_info->have_write_global_plte=MagickTrue;
14021 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14022 defined(PNG_MNG_FEATURES_SUPPORTED)
14023 mng_info->equal_palettes=MagickFalse;
14025 imageListLength=GetImageListLength(image);
14028 if (mng_info->adjoin)
14030 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14031 defined(PNG_MNG_FEATURES_SUPPORTED)
14033 If we aren't using a global palette for the entire MNG, check to
14034 see if we can use one for two or more consecutive images.
14036 if (need_local_plte && use_global_plte && !all_images_are_gray)
14038 if (mng_info->IsPalette)
14041 When equal_palettes is true, this image has the same palette
14042 as the previous PseudoClass image
14044 mng_info->have_write_global_plte=mng_info->equal_palettes;
14045 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14046 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14049 Write MNG PLTE chunk
14054 data_length=3*image->colors;
14055 (void) WriteBlobMSBULong(image,data_length);
14056 PNGType(chunk,mng_PLTE);
14057 LogPNGChunk(logging,mng_PLTE,data_length);
14059 for (i=0; i < (ssize_t) image->colors; i++)
14061 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14062 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14063 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14066 (void) WriteBlob(image,data_length+4,chunk);
14067 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14068 (uInt) (data_length+4)));
14069 mng_info->have_write_global_plte=MagickTrue;
14073 mng_info->have_write_global_plte=MagickFalse;
14084 previous_x=mng_info->page.x;
14085 previous_y=mng_info->page.y;
14092 mng_info->page=image->page;
14093 if ((mng_info->page.x != previous_x) ||
14094 (mng_info->page.y != previous_y))
14096 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
14097 PNGType(chunk,mng_DEFI);
14098 LogPNGChunk(logging,mng_DEFI,12L);
14099 chunk[4]=0; /* object 0 MSB */
14100 chunk[5]=0; /* object 0 LSB */
14101 chunk[6]=0; /* visible */
14102 chunk[7]=0; /* abstract */
14103 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14104 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14105 (void) WriteBlob(image,16,chunk);
14106 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14111 mng_info->write_mng=write_mng;
14113 if ((int) image->dispose >= 3)
14114 mng_info->framing_mode=3;
14116 if (mng_info->need_fram && mng_info->adjoin &&
14117 ((image->delay != mng_info->delay) ||
14118 (mng_info->framing_mode != mng_info->old_framing_mode)))
14120 if (image->delay == mng_info->delay)
14123 Write a MNG FRAM chunk with the new framing mode.
14125 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
14126 PNGType(chunk,mng_FRAM);
14127 LogPNGChunk(logging,mng_FRAM,1L);
14128 chunk[4]=(unsigned char) mng_info->framing_mode;
14129 (void) WriteBlob(image,5,chunk);
14130 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14135 Write a MNG FRAM chunk with the delay.
14137 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
14138 PNGType(chunk,mng_FRAM);
14139 LogPNGChunk(logging,mng_FRAM,10L);
14140 chunk[4]=(unsigned char) mng_info->framing_mode;
14141 chunk[5]=0; /* frame name separator (no name) */
14142 chunk[6]=2; /* flag for changing default delay */
14143 chunk[7]=0; /* flag for changing frame timeout */
14144 chunk[8]=0; /* flag for changing frame clipping */
14145 chunk[9]=0; /* flag for changing frame sync_id */
14146 PNGLong(chunk+10,(png_uint_32)
14147 ((mng_info->ticks_per_second*
14148 image->delay)/MagickMax(image->ticks_per_second,1)));
14149 (void) WriteBlob(image,14,chunk);
14150 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14151 mng_info->delay=(png_uint_32) image->delay;
14153 mng_info->old_framing_mode=mng_info->framing_mode;
14156 #if defined(JNG_SUPPORTED)
14157 if (image_info->compression == JPEGCompression)
14162 if (logging != MagickFalse)
14163 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14164 " Writing JNG object.");
14165 /* To do: specify the desired alpha compression method. */
14166 write_info=CloneImageInfo(image_info);
14167 write_info->compression=UndefinedCompression;
14168 status=WriteOneJNGImage(mng_info,write_info,image,exception);
14169 write_info=DestroyImageInfo(write_info);
14174 if (logging != MagickFalse)
14175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14176 " Writing PNG object.");
14178 mng_info->need_blob = MagickFalse;
14179 mng_info->ping_preserve_colormap = MagickFalse;
14181 /* We don't want any ancillary chunks written */
14182 mng_info->ping_exclude_bKGD=MagickTrue;
14183 mng_info->ping_exclude_caNv=MagickTrue;
14184 mng_info->ping_exclude_cHRM=MagickTrue;
14185 mng_info->ping_exclude_date=MagickTrue;
14186 mng_info->ping_exclude_EXIF=MagickTrue;
14187 mng_info->ping_exclude_gAMA=MagickTrue;
14188 mng_info->ping_exclude_iCCP=MagickTrue;
14189 /* mng_info->ping_exclude_iTXt=MagickTrue; */
14190 mng_info->ping_exclude_oFFs=MagickTrue;
14191 mng_info->ping_exclude_pHYs=MagickTrue;
14192 mng_info->ping_exclude_sRGB=MagickTrue;
14193 mng_info->ping_exclude_tEXt=MagickTrue;
14194 mng_info->ping_exclude_tRNS=MagickTrue;
14195 mng_info->ping_exclude_zCCP=MagickTrue;
14196 mng_info->ping_exclude_zTXt=MagickTrue;
14198 status=WriteOnePNGImage(mng_info,image_info,image,exception);
14201 if (status == MagickFalse)
14203 mng_info=MngInfoFreeStruct(mng_info);
14204 (void) CloseBlob(image);
14205 return(MagickFalse);
14207 (void) CatchImageException(image);
14208 if (GetNextImageInList(image) == (Image *) NULL)
14210 image=SyncNextImageInList(image);
14211 status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
14213 if (status == MagickFalse)
14216 } while (mng_info->adjoin);
14220 while (GetPreviousImageInList(image) != (Image *) NULL)
14221 image=GetPreviousImageInList(image);
14223 Write the MEND chunk.
14225 (void) WriteBlobMSBULong(image,0x00000000L);
14226 PNGType(chunk,mng_MEND);
14227 LogPNGChunk(logging,mng_MEND,0L);
14228 (void) WriteBlob(image,4,chunk);
14229 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14232 Relinquish resources.
14234 (void) CloseBlob(image);
14235 mng_info=MngInfoFreeStruct(mng_info);
14237 if (logging != MagickFalse)
14238 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14240 return(MagickTrue);
14242 #else /* PNG_LIBPNG_VER > 10011 */
14244 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14248 printf("Your PNG library is too old: You have libpng-%s\n",
14249 PNG_LIBPNG_VER_STRING);
14251 ThrowBinaryException(CoderError,"PNG library is too old",
14252 image_info->filename);
14255 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14258 return(WritePNGImage(image_info,image));
14260 #endif /* PNG_LIBPNG_VER > 10011 */