2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 % Read/Write Portable Network Graphics Image Format %
17 % Glenn Randers-Pehrson %
21 % Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % https://www.imagemagick.org/script/license.php %
29 % Unless required by applicable law or agreed to in writing, software %
30 % distributed under the License is distributed on an "AS IS" BASIS, %
31 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
32 % See the License for the specific language governing permissions and %
33 % limitations under the License. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colorspace.h"
57 #include "MagickCore/colorspace-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/enhance.h"
60 #include "MagickCore/exception.h"
61 #include "MagickCore/exception-private.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/histogram.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/layer.h"
67 #include "MagickCore/list.h"
68 #include "MagickCore/log.h"
69 #include "MagickCore/MagickCore.h"
70 #include "MagickCore/memory_.h"
71 #include "MagickCore/module.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/option.h"
75 #include "MagickCore/pixel.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/profile.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/semaphore.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/static.h"
84 #include "MagickCore/statistic.h"
85 #include "MagickCore/string_.h"
86 #include "MagickCore/string-private.h"
87 #include "MagickCore/transform.h"
88 #include "MagickCore/utility.h"
89 #if defined(MAGICKCORE_PNG_DELEGATE)
91 /* Suppress libpng pedantic warnings that were added in
92 * libpng-1.2.41 and libpng-1.4.0. If you are working on
93 * migration to libpng-1.5, remove these defines and then
94 * fix any code that generates warnings.
96 /* #define PNG_DEPRECATED Use of this function is deprecated */
97 /* #define PNG_USE_RESULT The result of this function must be checked */
98 /* #define PNG_NORETURN This function does not return */
99 /* #define PNG_ALLOCATED The result of the function is new memory */
100 /* #define PNG_DEPSTRUCT Access to this struct member is deprecated */
102 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
103 #define PNG_PTR_NORETURN
108 /* ImageMagick differences */
109 #define first_scene scene
111 #if PNG_LIBPNG_VER > 10011
113 Optional declarations. Define or undefine them as you like.
115 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
117 /* After eXIf chunk has been approved:
118 #define eXIf_SUPPORTED
121 /* Experimental; define one or both of these:
122 #define exIf_SUPPORTED
123 #define zxIf_SUPPORTED
124 #define zxIf_write_SUPPORTED
128 Features under construction. Define these to work on them.
130 #undef MNG_OBJECT_BUFFERS
131 #undef MNG_BASI_SUPPORTED
132 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
133 #define MNG_INSERT_LAYERS /* Troublesome, but seem to work as of 5.4.4 */
134 #if defined(MAGICKCORE_JPEG_DELEGATE)
135 # define JNG_SUPPORTED /* Not finished as of 5.5.2. See "To do" comments. */
137 #if !defined(RGBColorMatchExact)
138 #define IsPNGColorEqual(color,target) \
139 (((color).red == (target).red) && \
140 ((color).green == (target).green) && \
141 ((color).blue == (target).blue))
144 /* Table of recognized sRGB ICC profiles */
145 struct sRGB_info_struct
152 const struct sRGB_info_struct sRGB_info[] =
154 /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
155 { 3048, 0x3b8772b9UL, 0},
157 /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
158 { 3052, 0x427ebb21UL, 1},
160 /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
161 {60988, 0x306fd8aeUL, 0},
163 /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
164 {60960, 0xbbef7812UL, 0},
166 /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
167 { 3024, 0x5d5129ceUL, 1},
169 /* HP-Microsoft sRGB v2 perceptual */
170 { 3144, 0x182ea552UL, 0},
172 /* HP-Microsoft sRGB v2 media-relative */
173 { 3144, 0xf29e526dUL, 1},
175 /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
176 { 524, 0xd4938c39UL, 0},
178 /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
179 { 3212, 0x034af5a1UL, 0},
182 { 0, 0x00000000UL, 0},
185 /* Macros for left-bit-replication to ensure that pixels
186 * and PixelInfos all have the same image->depth, and for use
187 * in PNG8 quantization.
190 /* LBR01: Replicate top bit */
192 #define LBR01PacketRed(pixelpacket) \
193 (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
196 #define LBR01PacketGreen(pixelpacket) \
197 (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
200 #define LBR01PacketBlue(pixelpacket) \
201 (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
204 #define LBR01PacketAlpha(pixelpacket) \
205 (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
208 #define LBR01PacketRGB(pixelpacket) \
210 LBR01PacketRed((pixelpacket)); \
211 LBR01PacketGreen((pixelpacket)); \
212 LBR01PacketBlue((pixelpacket)); \
215 #define LBR01PacketRGBO(pixelpacket) \
217 LBR01PacketRGB((pixelpacket)); \
218 LBR01PacketAlpha((pixelpacket)); \
221 #define LBR01PixelRed(pixel) \
222 (SetPixelRed(image, \
223 ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
224 0 : QuantumRange,(pixel)));
226 #define LBR01PixelGreen(pixel) \
227 (SetPixelGreen(image, \
228 ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
229 0 : QuantumRange,(pixel)));
231 #define LBR01PixelBlue(pixel) \
232 (SetPixelBlue(image, \
233 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
234 0 : QuantumRange,(pixel)));
236 #define LBR01PixelAlpha(pixel) \
237 (SetPixelAlpha(image, \
238 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
239 0 : QuantumRange,(pixel)));
241 #define LBR01PixelRGB(pixel) \
243 LBR01PixelRed((pixel)); \
244 LBR01PixelGreen((pixel)); \
245 LBR01PixelBlue((pixel)); \
248 #define LBR01PixelRGBA(pixel) \
250 LBR01PixelRGB((pixel)); \
251 LBR01PixelAlpha((pixel)); \
254 /* LBR02: Replicate top 2 bits */
256 #define LBR02PacketRed(pixelpacket) \
258 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
259 (pixelpacket).red=ScaleCharToQuantum( \
260 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
262 #define LBR02PacketGreen(pixelpacket) \
264 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
265 (pixelpacket).green=ScaleCharToQuantum( \
266 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
268 #define LBR02PacketBlue(pixelpacket) \
270 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
271 (pixelpacket).blue=ScaleCharToQuantum( \
272 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
274 #define LBR02PacketAlpha(pixelpacket) \
276 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
277 (pixelpacket).alpha=ScaleCharToQuantum( \
278 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
281 #define LBR02PacketRGB(pixelpacket) \
283 LBR02PacketRed((pixelpacket)); \
284 LBR02PacketGreen((pixelpacket)); \
285 LBR02PacketBlue((pixelpacket)); \
288 #define LBR02PacketRGBO(pixelpacket) \
290 LBR02PacketRGB((pixelpacket)); \
291 LBR02PacketAlpha((pixelpacket)); \
294 #define LBR02PixelRed(pixel) \
296 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
298 SetPixelRed(image, ScaleCharToQuantum( \
299 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
302 #define LBR02PixelGreen(pixel) \
304 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
306 SetPixelGreen(image, ScaleCharToQuantum( \
307 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
310 #define LBR02PixelBlue(pixel) \
312 unsigned char lbr_bits= \
313 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
314 SetPixelBlue(image, ScaleCharToQuantum( \
315 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
318 #define LBR02PixelAlpha(pixel) \
320 unsigned char lbr_bits= \
321 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
322 SetPixelAlpha(image, ScaleCharToQuantum( \
323 (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
327 #define LBR02PixelRGB(pixel) \
329 LBR02PixelRed((pixel)); \
330 LBR02PixelGreen((pixel)); \
331 LBR02PixelBlue((pixel)); \
334 #define LBR02PixelRGBA(pixel) \
336 LBR02PixelRGB((pixel)); \
337 LBR02PixelAlpha((pixel)); \
340 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
341 PNG8 quantization) */
343 #define LBR03PacketRed(pixelpacket) \
345 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
346 (pixelpacket).red=ScaleCharToQuantum( \
347 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
349 #define LBR03PacketGreen(pixelpacket) \
351 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
352 (pixelpacket).green=ScaleCharToQuantum( \
353 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
355 #define LBR03PacketBlue(pixelpacket) \
357 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
358 (pixelpacket).blue=ScaleCharToQuantum( \
359 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
362 #define LBR03PacketRGB(pixelpacket) \
364 LBR03PacketRed((pixelpacket)); \
365 LBR03PacketGreen((pixelpacket)); \
366 LBR03PacketBlue((pixelpacket)); \
369 #define LBR03PixelRed(pixel) \
371 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
373 SetPixelRed(image, ScaleCharToQuantum( \
374 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
376 #define LBR03Green(pixel) \
378 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
380 SetPixelGreen(image, ScaleCharToQuantum( \
381 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
383 #define LBR03Blue(pixel) \
385 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
387 SetPixelBlue(image, ScaleCharToQuantum( \
388 (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
391 #define LBR03RGB(pixel) \
393 LBR03PixelRed((pixel)); \
394 LBR03Green((pixel)); \
395 LBR03Blue((pixel)); \
398 /* LBR04: Replicate top 4 bits */
400 #define LBR04PacketRed(pixelpacket) \
402 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
403 (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
405 #define LBR04PacketGreen(pixelpacket) \
407 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
408 (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
410 #define LBR04PacketBlue(pixelpacket) \
412 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
413 (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
415 #define LBR04PacketAlpha(pixelpacket) \
417 unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
418 (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
421 #define LBR04PacketRGB(pixelpacket) \
423 LBR04PacketRed((pixelpacket)); \
424 LBR04PacketGreen((pixelpacket)); \
425 LBR04PacketBlue((pixelpacket)); \
428 #define LBR04PacketRGBO(pixelpacket) \
430 LBR04PacketRGB((pixelpacket)); \
431 LBR04PacketAlpha((pixelpacket)); \
434 #define LBR04PixelRed(pixel) \
436 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
439 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
441 #define LBR04PixelGreen(pixel) \
443 unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
445 SetPixelGreen(image,\
446 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
448 #define LBR04PixelBlue(pixel) \
450 unsigned char lbr_bits= \
451 ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
453 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
455 #define LBR04PixelAlpha(pixel) \
457 unsigned char lbr_bits= \
458 ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
459 SetPixelAlpha(image,\
460 ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
463 #define LBR04PixelRGB(pixel) \
465 LBR04PixelRed((pixel)); \
466 LBR04PixelGreen((pixel)); \
467 LBR04PixelBlue((pixel)); \
470 #define LBR04PixelRGBA(pixel) \
472 LBR04PixelRGB((pixel)); \
473 LBR04PixelAlpha((pixel)); \
477 Establish thread safety.
478 setjmp/longjmp is claimed to be safe on these platforms:
479 setjmp/longjmp is alleged to be unsafe on these platforms:
481 #ifdef PNG_SETJMP_SUPPORTED
482 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
483 # define IMPNG_SETJMP_NOT_THREAD_SAFE
486 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
488 *ping_semaphore = (SemaphoreInfo *) NULL;
493 This temporary until I set up malloc'ed object attributes array.
494 Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
497 #define MNG_MAX_OBJECTS 256
500 If this not defined, spec is interpreted strictly. If it is
501 defined, an attempt will be made to recover from some errors,
503 o global PLTE too short
508 Don't try to define PNG_MNG_FEATURES_SUPPORTED here. Make sure
509 it's defined in libpng/pngconf.h, version 1.0.9 or later. It won't work
510 with earlier versions of libpng. From libpng-1.0.3a to libpng-1.0.8,
511 PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
512 libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
513 PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
514 will be enabled by default in libpng-1.2.0.
516 #ifdef PNG_MNG_FEATURES_SUPPORTED
517 # ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
518 # define PNG_READ_EMPTY_PLTE_SUPPORTED
520 # ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
521 # define PNG_WRITE_EMPTY_PLTE_SUPPORTED
526 Maximum valid size_t in PNG/MNG chunks is (2^31)-1
527 This macro is only defined in libpng-1.0.3 and later.
528 Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
530 #ifndef PNG_UINT_31_MAX
531 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
535 Constant strings for known chunk types. If you need to add a chunk,
536 add a string holding the name here. To make the code more
537 portable, we use ASCII numbers like this, not characters.
540 /* until registration of eXIf */
541 static const png_byte mng_exIf[5]={101, 120, 73, 102, (png_byte) '\0'};
543 /* after registration of eXIf */
544 static const png_byte mng_eXIf[5]={101, 88, 73, 102, (png_byte) '\0'};
546 static const png_byte mng_MHDR[5]={ 77, 72, 68, 82, (png_byte) '\0'};
547 static const png_byte mng_BACK[5]={ 66, 65, 67, 75, (png_byte) '\0'};
548 static const png_byte mng_BASI[5]={ 66, 65, 83, 73, (png_byte) '\0'};
549 static const png_byte mng_CLIP[5]={ 67, 76, 73, 80, (png_byte) '\0'};
550 static const png_byte mng_CLON[5]={ 67, 76, 79, 78, (png_byte) '\0'};
551 static const png_byte mng_DEFI[5]={ 68, 69, 70, 73, (png_byte) '\0'};
552 static const png_byte mng_DHDR[5]={ 68, 72, 68, 82, (png_byte) '\0'};
553 static const png_byte mng_DISC[5]={ 68, 73, 83, 67, (png_byte) '\0'};
554 static const png_byte mng_ENDL[5]={ 69, 78, 68, 76, (png_byte) '\0'};
555 static const png_byte mng_FRAM[5]={ 70, 82, 65, 77, (png_byte) '\0'};
556 static const png_byte mng_IEND[5]={ 73, 69, 78, 68, (png_byte) '\0'};
557 static const png_byte mng_IHDR[5]={ 73, 72, 68, 82, (png_byte) '\0'};
558 static const png_byte mng_JHDR[5]={ 74, 72, 68, 82, (png_byte) '\0'};
559 static const png_byte mng_LOOP[5]={ 76, 79, 79, 80, (png_byte) '\0'};
560 static const png_byte mng_MAGN[5]={ 77, 65, 71, 78, (png_byte) '\0'};
561 static const png_byte mng_MEND[5]={ 77, 69, 78, 68, (png_byte) '\0'};
562 static const png_byte mng_MOVE[5]={ 77, 79, 86, 69, (png_byte) '\0'};
563 static const png_byte mng_PAST[5]={ 80, 65, 83, 84, (png_byte) '\0'};
564 static const png_byte mng_PLTE[5]={ 80, 76, 84, 69, (png_byte) '\0'};
565 static const png_byte mng_SAVE[5]={ 83, 65, 86, 69, (png_byte) '\0'};
566 static const png_byte mng_SEEK[5]={ 83, 69, 69, 75, (png_byte) '\0'};
567 static const png_byte mng_SHOW[5]={ 83, 72, 79, 87, (png_byte) '\0'};
568 static const png_byte mng_TERM[5]={ 84, 69, 82, 77, (png_byte) '\0'};
569 static const png_byte mng_bKGD[5]={ 98, 75, 71, 68, (png_byte) '\0'};
570 static const png_byte mng_caNv[5]={ 99, 97, 78, 118, (png_byte) '\0'};
571 static const png_byte mng_cHRM[5]={ 99, 72, 82, 77, (png_byte) '\0'};
572 static const png_byte mng_gAMA[5]={103, 65, 77, 65, (png_byte) '\0'};
573 static const png_byte mng_iCCP[5]={105, 67, 67, 80, (png_byte) '\0'};
574 static const png_byte mng_nEED[5]={110, 69, 69, 68, (png_byte) '\0'};
575 static const png_byte mng_pHYg[5]={112, 72, 89, 103, (png_byte) '\0'};
576 static const png_byte mng_vpAg[5]={118, 112, 65, 103, (png_byte) '\0'};
577 static const png_byte mng_pHYs[5]={112, 72, 89, 115, (png_byte) '\0'};
578 static const png_byte mng_sBIT[5]={115, 66, 73, 84, (png_byte) '\0'};
579 static const png_byte mng_sRGB[5]={115, 82, 71, 66, (png_byte) '\0'};
580 static const png_byte mng_tRNS[5]={116, 82, 78, 83, (png_byte) '\0'};
581 #if defined(zxIf_SUPPORTED)
582 static const png_byte mng_uxIf[5]={117, 120, 73, 102, (png_byte) '\0'};
583 static const png_byte mng_zxIf[5]={122, 120, 73, 102, (png_byte) '\0'};
586 #if defined(JNG_SUPPORTED)
587 static const png_byte mng_IDAT[5]={ 73, 68, 65, 84, (png_byte) '\0'};
588 static const png_byte mng_JDAT[5]={ 74, 68, 65, 84, (png_byte) '\0'};
589 static const png_byte mng_JDAA[5]={ 74, 68, 65, 65, (png_byte) '\0'};
590 static const png_byte mng_JdAA[5]={ 74, 100, 65, 65, (png_byte) '\0'};
591 static const png_byte mng_JSEP[5]={ 74, 83, 69, 80, (png_byte) '\0'};
592 static const png_byte mng_oFFs[5]={111, 70, 70, 115, (png_byte) '\0'};
596 /* Other known chunks that are not yet supported by ImageMagick: */
597 static const png_byte mng_hIST[5]={104, 73, 83, 84, (png_byte) '\0'};
598 static const png_byte mng_iTXt[5]={105, 84, 88, 116, (png_byte) '\0'};
599 static const png_byte mng_sPLT[5]={115, 80, 76, 84, (png_byte) '\0'};
600 static const png_byte mng_sTER[5]={115, 84, 69, 82, (png_byte) '\0'};
601 static const png_byte mng_tEXt[5]={116, 69, 88, 116, (png_byte) '\0'};
602 static const png_byte mng_tIME[5]={116, 73, 77, 69, (png_byte) '\0'};
603 static const png_byte mng_zTXt[5]={122, 84, 88, 116, (png_byte) '\0'};
606 typedef struct _MngBox
615 typedef struct _MngPair
622 #ifdef MNG_OBJECT_BUFFERS
623 typedef struct _MngBuffer
655 typedef struct _MngInfo
658 #ifdef MNG_OBJECT_BUFFERS
660 *ob[MNG_MAX_OBJECTS];
671 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
672 bytes_in_read_buffer,
678 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
679 defined(PNG_MNG_FEATURES_SUPPORTED)
691 have_saved_bkgd_index,
692 have_write_global_chrm,
693 have_write_global_gama,
694 have_write_global_plte,
695 have_write_global_srgb,
709 x_off[MNG_MAX_OBJECTS],
710 y_off[MNG_MAX_OBJECTS];
716 object_clip[MNG_MAX_OBJECTS];
719 /* These flags could be combined into one byte */
720 exists[MNG_MAX_OBJECTS],
721 frozen[MNG_MAX_OBJECTS],
723 invisible[MNG_MAX_OBJECTS],
724 viewable[MNG_MAX_OBJECTS];
736 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
754 global_x_pixels_per_unit,
755 global_y_pixels_per_unit,
765 global_phys_unit_type,
780 write_png_compression_level,
781 write_png_compression_strategy,
782 write_png_compression_filter,
789 #ifdef MNG_BASI_SUPPORTED
797 basi_compression_method,
799 basi_interlace_method,
822 /* Added at version 6.6.6-7 */
831 /* ping_exclude_iTXt, */
839 ping_exclude_zCCP, /* hex-encoded iCCP */
841 ping_preserve_colormap,
842 /* Added at version 6.8.5-7 */
844 /* Added at version 6.8.9-9 */
851 Forward declarations.
853 static MagickBooleanType
854 WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
856 static MagickBooleanType
857 WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
859 #if defined(JNG_SUPPORTED)
860 static MagickBooleanType
861 WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
864 #if PNG_LIBPNG_VER > 10011
867 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
868 static MagickBooleanType
869 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
871 /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
873 * This is true if the high byte and the next highest byte of
874 * each sample of the image, the colormap, and the background color
875 * are equal to each other. We check this by seeing if the samples
876 * are unchanged when we scale them down to 8 and back up to Quantum.
878 * We don't use the method GetImageDepth() because it doesn't check
879 * background and doesn't handle PseudoClass specially.
882 #define QuantumToCharToQuantumEqQuantum(quantum) \
883 ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
886 ok_to_reduce=MagickFalse;
888 if (image->depth >= 16)
895 QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
896 QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
897 QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
898 MagickTrue : MagickFalse;
900 if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
904 for (indx=0; indx < (ssize_t) image->colors; indx++)
907 QuantumToCharToQuantumEqQuantum(
908 image->colormap[indx].red) &&
909 QuantumToCharToQuantumEqQuantum(
910 image->colormap[indx].green) &&
911 QuantumToCharToQuantumEqQuantum(
912 image->colormap[indx].blue)) ?
913 MagickTrue : MagickFalse;
915 if (ok_to_reduce == MagickFalse)
920 if ((ok_to_reduce != MagickFalse) &&
921 (image->storage_class != PseudoClass))
929 for (y=0; y < (ssize_t) image->rows; y++)
931 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
933 if (p == (const Quantum *) NULL)
935 ok_to_reduce = MagickFalse;
939 for (x=(ssize_t) image->columns-1; x >= 0; x--)
942 QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
943 QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
944 QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
945 MagickTrue : MagickFalse;
947 if (ok_to_reduce == MagickFalse)
950 p+=GetPixelChannels(image);
957 if (ok_to_reduce != MagickFalse)
959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
960 " OK to reduce PNG bit depth to 8 without loss of info");
964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
965 " Not OK to reduce PNG bit depth to 8 without losing info");
971 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
973 static const char* PngColorTypeToString(const unsigned int color_type)
980 case PNG_COLOR_TYPE_GRAY:
983 case PNG_COLOR_TYPE_GRAY_ALPHA:
984 result = "Gray+Alpha";
986 case PNG_COLOR_TYPE_PALETTE:
989 case PNG_COLOR_TYPE_RGB:
992 case PNG_COLOR_TYPE_RGB_ALPHA:
993 result = "RGB+Alpha";
1001 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1005 case PerceptualIntent:
1008 case RelativeIntent:
1011 case SaturationIntent:
1014 case AbsoluteIntent:
1022 static RenderingIntent
1023 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1025 switch (ping_intent)
1028 return PerceptualIntent;
1031 return RelativeIntent;
1034 return SaturationIntent;
1037 return AbsoluteIntent;
1040 return UndefinedIntent;
1045 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1047 switch (ping_intent)
1050 return "Perceptual Intent";
1053 return "Relative Intent";
1056 return "Saturation Intent";
1059 return "Absolute Intent";
1062 return "Undefined Intent";
1067 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1069 switch (ping_colortype)
1087 return "UndefinedColorType";
1091 #endif /* PNG_LIBPNG_VER > 10011 */
1092 #endif /* MAGICKCORE_PNG_DELEGATE */
1095 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1105 % IsMNG() returns MagickTrue if the image format type, identified by the
1106 % magick string, is MNG.
1108 % The format of the IsMNG method is:
1110 % MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1112 % A description of each parameter follows:
1114 % o magick: compare image format pattern against these bytes.
1116 % o length: Specifies the length of the magick string.
1120 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1123 return(MagickFalse);
1125 if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1128 return(MagickFalse);
1132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1140 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1142 % IsJNG() returns MagickTrue if the image format type, identified by the
1143 % magick string, is JNG.
1145 % The format of the IsJNG method is:
1147 % MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1149 % A description of each parameter follows:
1151 % o magick: compare image format pattern against these bytes.
1153 % o length: Specifies the length of the magick string.
1157 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1160 return(MagickFalse);
1162 if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1165 return(MagickFalse);
1169 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179 % IsPNG() returns MagickTrue if the image format type, identified by the
1180 % magick string, is PNG.
1182 % The format of the IsPNG method is:
1184 % MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1186 % A description of each parameter follows:
1188 % o magick: compare image format pattern against these bytes.
1190 % o length: Specifies the length of the magick string.
1193 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1196 return(MagickFalse);
1198 if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1201 return(MagickFalse);
1204 #if defined(MAGICKCORE_PNG_DELEGATE)
1205 #if defined(__cplusplus) || defined(c_plusplus)
1209 #if (PNG_LIBPNG_VER > 10011)
1210 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1215 assert(image != (Image *) NULL);
1216 assert(image->signature == MagickCoreSignature);
1217 buffer[0]=(unsigned char) (value >> 24);
1218 buffer[1]=(unsigned char) (value >> 16);
1219 buffer[2]=(unsigned char) (value >> 8);
1220 buffer[3]=(unsigned char) value;
1221 return((size_t) WriteBlob(image,4,buffer));
1224 static void PNGLong(png_bytep p,png_uint_32 value)
1226 *p++=(png_byte) ((value >> 24) & 0xff);
1227 *p++=(png_byte) ((value >> 16) & 0xff);
1228 *p++=(png_byte) ((value >> 8) & 0xff);
1229 *p++=(png_byte) (value & 0xff);
1232 static void PNGsLong(png_bytep p,png_int_32 value)
1234 *p++=(png_byte) ((value >> 24) & 0xff);
1235 *p++=(png_byte) ((value >> 16) & 0xff);
1236 *p++=(png_byte) ((value >> 8) & 0xff);
1237 *p++=(png_byte) (value & 0xff);
1240 static void PNGShort(png_bytep p,png_uint_16 value)
1242 *p++=(png_byte) ((value >> 8) & 0xff);
1243 *p++=(png_byte) (value & 0xff);
1246 static void PNGType(png_bytep p,const png_byte *type)
1248 (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1251 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1254 if (logging != MagickFalse)
1255 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1256 " Writing %c%c%c%c chunk, length: %.20g",
1257 type[0],type[1],type[2],type[3],(double) length);
1259 #endif /* PNG_LIBPNG_VER > 10011 */
1261 #if defined(__cplusplus) || defined(c_plusplus)
1265 #if PNG_LIBPNG_VER > 10011
1267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1271 % R e a d P N G I m a g e %
1275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1277 % ReadPNGImage() reads a Portable Network Graphics (PNG) or
1278 % Multiple-image Network Graphics (MNG) image file and returns it. It
1279 % allocates the memory necessary for the new Image structure and returns a
1280 % pointer to the new image or set of images.
1282 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
1284 % The format of the ReadPNGImage method is:
1286 % Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1288 % A description of each parameter follows:
1290 % o image_info: the image info.
1292 % o exception: return any errors or warnings in this structure.
1294 % To do, more or less in chronological order (as of version 5.5.2,
1295 % November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1297 % Get 16-bit cheap transparency working.
1299 % (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1301 % Preserve all unknown and not-yet-handled known chunks found in input
1302 % PNG file and copy them into output PNG files according to the PNG
1305 % (At this point, PNG encoding should be in full MNG compliance)
1307 % Provide options for choice of background to use when the MNG BACK
1308 % chunk is not present or is not mandatory (i.e., leave transparent,
1309 % user specified, MNG BACK, PNG bKGD)
1311 % Implement LOOP/ENDL [done, but could do discretionary loops more
1312 % efficiently by linking in the duplicate frames.].
1314 % Decode and act on the MHDR simplicity profile (offer option to reject
1315 % files or attempt to process them anyway when the profile isn't LC or VLC).
1317 % Upgrade to full MNG without Delta-PNG.
1319 % o BACK [done a while ago except for background image ID]
1320 % o MOVE [done 15 May 1999]
1321 % o CLIP [done 15 May 1999]
1322 % o DISC [done 19 May 1999]
1323 % o SAVE [partially done 19 May 1999 (marks objects frozen)]
1324 % o SEEK [partially done 19 May 1999 (discard function only)]
1328 % o MNG-level tEXt/iTXt/zTXt
1333 % o iTXt (wait for libpng implementation).
1335 % Use the scene signature to discover when an identical scene is
1336 % being reused, and just point to the original image->exception instead
1337 % of storing another set of pixels. This not specific to MNG
1338 % but could be applied generally.
1340 % Upgrade to full MNG with Delta-PNG.
1342 % JNG tEXt/iTXt/zTXt
1344 % We will not attempt to read files containing the CgBI chunk.
1345 % They are really Xcode files meant for display on the iPhone.
1346 % These are not valid PNG files and it is impossible to recover
1347 % the original PNG from files that have been converted to Xcode-PNG,
1348 % since irretrievable loss of color data has occurred due to the
1349 % use of premultiplied alpha.
1352 #if defined(__cplusplus) || defined(c_plusplus)
1357 This the function that does the actual reading of data. It is
1358 the same as the one supplied in libpng, except that it receives the
1359 datastream from the ReadBlob() function instead of standard input.
1361 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1366 image=(Image *) png_get_io_ptr(png_ptr);
1372 check=(png_size_t) ReadBlob(image,(size_t) length,data);
1373 if (check != length)
1376 msg[MagickPathExtent];
1378 (void) FormatLocaleString(msg,MagickPathExtent,
1379 "Expected %.20g bytes; found %.20g bytes",(double) length,
1381 png_warning(png_ptr,msg);
1382 png_error(png_ptr,"Read Exception");
1387 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1388 !defined(PNG_MNG_FEATURES_SUPPORTED)
1389 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1390 * older than libpng-1.0.3a, which was the first to allow the empty
1391 * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1392 * ifdef'ed out. Earlier versions would crash if the bKGD chunk was
1393 * encountered after an empty PLTE, so we have to look ahead for bKGD
1394 * chunks and remove them from the datastream that is passed to libpng,
1395 * and store their contents for later use.
1397 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1412 mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1413 image=(Image *) mng_info->image;
1414 while (mng_info->bytes_in_read_buffer && length)
1416 data[i]=mng_info->read_buffer[i];
1417 mng_info->bytes_in_read_buffer--;
1423 check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1425 if (check != length)
1426 png_error(png_ptr,"Read Exception");
1430 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1433 check=(png_size_t) ReadBlob(image,(size_t) length,
1434 (char *) mng_info->read_buffer);
1435 mng_info->read_buffer[4]=0;
1436 mng_info->bytes_in_read_buffer=4;
1437 if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1438 mng_info->found_empty_plte=MagickTrue;
1439 if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1441 mng_info->found_empty_plte=MagickFalse;
1442 mng_info->have_saved_bkgd_index=MagickFalse;
1446 if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1449 check=(png_size_t) ReadBlob(image,(size_t) length,
1450 (char *) mng_info->read_buffer);
1451 mng_info->read_buffer[4]=0;
1452 mng_info->bytes_in_read_buffer=4;
1453 if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1454 if (mng_info->found_empty_plte)
1457 Skip the bKGD data byte and CRC.
1460 ReadBlob(image,5,(char *) mng_info->read_buffer);
1461 check=(png_size_t) ReadBlob(image,(size_t) length,
1462 (char *) mng_info->read_buffer);
1463 mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1464 mng_info->have_saved_bkgd_index=MagickTrue;
1465 mng_info->bytes_in_read_buffer=0;
1473 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1478 image=(Image *) png_get_io_ptr(png_ptr);
1484 check=(png_size_t) WriteBlob(image,(size_t) length,data);
1486 if (check != length)
1487 png_error(png_ptr,"WriteBlob Failed");
1491 static void png_flush_data(png_structp png_ptr)
1496 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1497 static int PalettesAreEqual(Image *a,Image *b)
1502 if ((a == (Image *) NULL) || (b == (Image *) NULL))
1503 return((int) MagickFalse);
1505 if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1506 return((int) MagickFalse);
1508 if (a->colors != b->colors)
1509 return((int) MagickFalse);
1511 for (i=0; i < (ssize_t) a->colors; i++)
1513 if ((a->colormap[i].red != b->colormap[i].red) ||
1514 (a->colormap[i].green != b->colormap[i].green) ||
1515 (a->colormap[i].blue != b->colormap[i].blue))
1516 return((int) MagickFalse);
1519 return((int) MagickTrue);
1523 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1525 if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1526 mng_info->exists[i] && !mng_info->frozen[i])
1528 #ifdef MNG_OBJECT_BUFFERS
1529 if (mng_info->ob[i] != (MngBuffer *) NULL)
1531 if (mng_info->ob[i]->reference_count > 0)
1532 mng_info->ob[i]->reference_count--;
1534 if (mng_info->ob[i]->reference_count == 0)
1536 if (mng_info->ob[i]->image != (Image *) NULL)
1537 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1539 mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1542 mng_info->ob[i]=(MngBuffer *) NULL;
1544 mng_info->exists[i]=MagickFalse;
1545 mng_info->invisible[i]=MagickFalse;
1546 mng_info->viewable[i]=MagickFalse;
1547 mng_info->frozen[i]=MagickFalse;
1548 mng_info->x_off[i]=0;
1549 mng_info->y_off[i]=0;
1550 mng_info->object_clip[i].left=0;
1551 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1552 mng_info->object_clip[i].top=0;
1553 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1557 static void MngInfoFreeStruct(MngInfo *mng_info,
1558 MagickBooleanType *have_mng_structure)
1560 if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1565 for (i=1; i < MNG_MAX_OBJECTS; i++)
1566 MngInfoDiscardObject(mng_info,i);
1568 if (mng_info->global_plte != (png_colorp) NULL)
1569 mng_info->global_plte=(png_colorp)
1570 RelinquishMagickMemory(mng_info->global_plte);
1572 mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1573 *have_mng_structure=MagickFalse;
1577 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1583 if (box.left < box2.left)
1586 if (box.top < box2.top)
1589 if (box.right > box2.right)
1590 box.right=box2.right;
1592 if (box.bottom > box2.bottom)
1593 box.bottom=box2.bottom;
1598 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1605 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1607 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1608 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1609 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1610 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1611 if (delta_type != 0)
1613 box.left+=previous_box.left;
1614 box.right+=previous_box.right;
1615 box.top+=previous_box.top;
1616 box.bottom+=previous_box.bottom;
1622 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1628 Read two ssize_ts from CLON, MOVE or PAST chunk
1630 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1631 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1633 if (delta_type != 0)
1635 pair.a+=previous_pair.a;
1636 pair.b+=previous_pair.b;
1642 static long mng_get_long(unsigned char *p)
1644 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1647 typedef struct _PNGErrorInfo
1656 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1667 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1668 image=error_info->image;
1669 exception=error_info->exception;
1671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1672 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1674 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1675 "`%s'",image->filename);
1677 #if (PNG_LIBPNG_VER < 10500)
1678 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1679 * are building with libpng-1.4.x and can be ignored.
1681 longjmp(ping->jmpbuf,1);
1683 png_longjmp(ping,1);
1687 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1698 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1699 png_error(ping, message);
1701 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1702 image=error_info->image;
1703 exception=error_info->exception;
1704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1705 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1707 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1708 message,"`%s'",image->filename);
1711 #ifdef PNG_USER_MEM_SUPPORTED
1712 #if PNG_LIBPNG_VER >= 10400
1713 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1715 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1719 return((png_voidp) AcquireMagickMemory((size_t) size));
1723 Free a pointer. It is removed from the list at the same time.
1725 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1728 ptr=RelinquishMagickMemory(ptr);
1729 return((png_free_ptr) NULL);
1733 #if defined(__cplusplus) || defined(c_plusplus)
1738 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1739 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1744 register unsigned char
1758 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1759 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1760 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1761 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1762 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1766 /* look for newline */
1770 /* look for length */
1771 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1774 length=(png_uint_32) StringToLong(sp);
1776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1777 " length: %lu",(unsigned long) length);
1779 while (*sp != ' ' && *sp != '\n')
1782 /* allocate space */
1785 png_warning(ping,"invalid profile length");
1786 return(MagickFalse);
1789 profile=BlobToStringInfo((const void *) NULL,length);
1791 if (profile == (StringInfo *) NULL)
1793 png_warning(ping, "unable to copy profile");
1794 return(MagickFalse);
1797 /* copy profile, skipping white space and column 1 "=" signs */
1798 dp=GetStringInfoDatum(profile);
1801 for (i=0; i < (ssize_t) nibbles; i++)
1803 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1807 png_warning(ping, "ran out of profile data");
1808 profile=DestroyStringInfo(profile);
1809 return(MagickFalse);
1815 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1818 (*dp++)+=unhex[(int) *sp++];
1821 We have already read "Raw profile type.
1823 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1824 profile=DestroyStringInfo(profile);
1826 if (image_info->verbose)
1827 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1832 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1833 #ifdef zxIf_SUPPORTED
1835 /* exif_inf() was derived from zlib-1.2.11/examples/zpipe.c/inf()
1836 Not copyrighted -- provided to the public domain
1837 Version 1.4 11 December 2005 Mark Adler */
1841 /* Decompress from source to dest (copied from zlib-1.2.11/examples).
1842 inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
1843 allocated for processing, Z_DATA_ERROR if the deflate data is
1844 invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
1845 the version of the library linked do not match, or Z_ERRNO if there
1846 is an error reading or writing the files. */
1848 int exif_inf(png_structp png_ptr, unsigned char *source,
1849 unsigned char **dest, size_t n, png_uint_32 inflated_size)
1851 /* *source: compressed data stream (input)
1852 *dest: inflated data (output)
1855 Returns one of the following:
1856 return(-1); chunk had an error
1857 return(n); success, n is length of inflated data
1863 size_t inflated_length = inflated_size;
1865 if (inflated_length >= PNG_USER_CHUNK_MALLOC_MAX - 1U ||
1866 inflated_length == 0)
1870 #if PNG_LIBPNG_VER >= 14000
1871 *dest=(unsigned char *) png_malloc(png_ptr,
1872 (png_alloc_size_t) inflated_length);
1874 *dest=(unsigned char *) png_malloc(png_ptr,
1875 (png_size_t) inflated_length);
1877 /* allocate inflate state */
1878 strm.zalloc = Z_NULL;
1879 strm.zfree = Z_NULL;
1880 strm.opaque = Z_NULL;
1882 strm.next_in = Z_NULL;
1883 ret = inflateInit(&strm);
1886 /* decompress until deflate stream ends or end of file */
1888 strm.avail_in = (int)n;
1889 strm.next_in = source;
1891 /* run inflate() on input until output buffer not full */
1893 strm.avail_out = (int)inflated_length;
1894 strm.next_out = *dest;
1895 ret = inflate(&strm, Z_NO_FLUSH);
1896 assert(ret != Z_STREAM_ERROR); /* state not clobbered */
1901 (void)inflateEnd(&strm);
1904 } while (strm.avail_out == 0);
1905 /* done when inflate() says it's done */
1906 } while (ret != Z_STREAM_END);
1908 /* clean up and return */
1910 /* To do: take care of too little or too much data */
1912 (void)inflateEnd(&strm);
1913 return (inflated_length);
1917 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1923 /* The unknown chunk structure contains the chunk data:
1928 Note that libpng has already taken care of the CRC handling.
1930 Returns one of the following:
1931 return(-n); chunk had an error
1932 return(0); did not recognize
1936 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1937 " read_user_chunk: found %c%c%c%c chunk",
1938 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1940 #if defined(zxIf_SUPPORTED)
1941 if ((chunk->name[0] == 122 || chunk->name[0] == 117 ) &&
1942 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
1943 chunk->name[2] == 73 &&
1944 chunk-> name[3] == 102)
1946 /* process uxIf or zxIf chunk */
1962 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1963 " recognized uxIf|zxIf chunk");
1965 image=(Image *) png_get_user_chunk_ptr(ping);
1967 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1969 profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1971 if (profile == (StringInfo *) NULL)
1973 (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1974 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1978 p=GetStringInfoDatum(profile);
1980 /* Initialize profile with "Exif\0\0" */
1988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1989 " initialized uxIf|zxIf chunk");
1991 switch (chunk->data[0])
1999 /* copy chunk->data to profile */
2001 for (i=0; i<chunk->size; i++)
2004 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2005 " SetImageProfile with %lu bytes",
2006 (unsigned long) chunk->size+6);
2007 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2008 (void) SetImageProfile(image,"exif",profile,
2009 error_info->exception);
2014 /* Zlib compressed */
2019 png_uint_32 inflated_size;
2021 png_free(ping,profile);
2023 if (chunk->size < 5)
2027 s++; // skip compression byte
2029 inflated_size = (png_uint_32)
2030 (((s[0] & 0xff) << 24) | ((s[1] & 0xff) << 16) |
2031 ((s[2] & 0xff) << 8) | ((s[3] & 0xff) ));
2035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2036 " inflated_size = %lu bytes",inflated_size);
2038 /* uncompress chunk->data to temporary profile */
2039 inflated_size=exif_inf(ping,s,&temp,chunk->size-1,inflated_size);
2041 if (inflated_size <= 0)
2043 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2044 " inflated_size = %lu bytes",inflated_size);
2047 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2049 profile=BlobToStringInfo((const void *) NULL,inflated_size+6);
2051 if (profile == (StringInfo *) NULL)
2053 (void) ThrowMagickException(error_info->exception,
2055 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2059 p=GetStringInfoDatum(profile);
2060 /* Initialize profile with "Exif\0\0" */
2068 for (i=0; i<inflated_size; i++)
2071 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2072 " SetImageProfile with %lu bytes",
2073 (unsigned long) inflated_size+6);
2074 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2075 (void) SetImageProfile(image,"exif",profile,
2076 error_info->exception);
2078 png_free(ping,temp);
2084 #endif /* zxIf_SUPPORTED */
2086 if (chunk->name[0] == 101 &&
2087 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
2088 chunk->name[2] == 73 &&
2089 chunk-> name[3] == 102)
2091 /* process eXIf or exIf chunk */
2108 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2109 " recognized eXIf|exIf chunk");
2111 image=(Image *) png_get_user_chunk_ptr(ping);
2113 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2115 profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
2117 if (profile == (StringInfo *) NULL)
2119 (void) ThrowMagickException(error_info->exception,GetMagickModule(),
2120 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2124 p=GetStringInfoDatum(profile);
2126 /* Initialize profile with "Exif\0\0" */
2134 /* copy chunk->data to profile */
2136 for (i=0; i<chunk->size; i++)
2139 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2140 (void) SetImageProfile(image,"exif",profile,
2141 error_info->exception);
2146 /* vpAg (deprecated, replaced by caNv) */
2147 if (chunk->name[0] == 118 &&
2148 chunk->name[1] == 112 &&
2149 chunk->name[2] == 65 &&
2150 chunk->name[3] == 103)
2152 /* recognized vpAg */
2154 if (chunk->size != 9)
2155 return(-1); /* Error return */
2157 if (chunk->data[8] != 0)
2158 return(0); /* ImageMagick requires pixel units */
2160 image=(Image *) png_get_user_chunk_ptr(ping);
2162 image->page.width=(size_t) ((chunk->data[0] << 24) |
2163 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2165 image->page.height=(size_t) ((chunk->data[4] << 24) |
2166 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2172 if (chunk->name[0] == 99 &&
2173 chunk->name[1] == 97 &&
2174 chunk->name[2] == 78 &&
2175 chunk->name[3] == 118)
2177 /* recognized caNv */
2179 if (chunk->size != 16)
2180 return(-1); /* Error return */
2182 image=(Image *) png_get_user_chunk_ptr(ping);
2184 image->page.width=(size_t) ((chunk->data[0] << 24) |
2185 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2187 image->page.height=(size_t) ((chunk->data[4] << 24) |
2188 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2190 image->page.x=(size_t) ((chunk->data[8] << 24) |
2191 (chunk->data[9] << 16) | (chunk->data[10] << 8) | chunk->data[11]);
2193 image->page.y=(size_t) ((chunk->data[12] << 24) |
2194 (chunk->data[13] << 16) | (chunk->data[14] << 8) | chunk->data[15]);
2199 return(0); /* Did not recognize */
2201 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2203 #if defined(PNG_tIME_SUPPORTED)
2204 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2205 ExceptionInfo *exception)
2210 if (png_get_tIME(ping,info,&time))
2215 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2216 time->year,time->month,time->day,time->hour,time->minute,time->second);
2217 SetImageProperty(image,"png:tIME",timestamp,exception);
2223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2227 % R e a d O n e P N G I m a g e %
2231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2233 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2234 % (minus the 8-byte signature) and returns it. It allocates the memory
2235 % necessary for the new Image structure and returns a pointer to the new
2238 % The format of the ReadOnePNGImage method is:
2240 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2241 % ExceptionInfo *exception)
2243 % A description of each parameter follows:
2245 % o mng_info: Specifies a pointer to a MngInfo structure.
2247 % o image_info: the image info.
2249 % o exception: return any errors or warnings in this structure.
2252 static Image *ReadOnePNGImage(MngInfo *mng_info,
2253 const ImageInfo *image_info, ExceptionInfo *exception)
2255 /* Read one PNG image */
2257 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2270 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2280 ping_interlace_method,
2281 ping_compression_method,
2295 ping_found_sRGB_cHRM,
2300 *volatile pixel_info;
2338 register unsigned char
2358 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2359 png_byte unused_chunks[]=
2361 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2362 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2363 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2364 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2365 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2366 #if !defined(PNG_tIME_SUPPORTED)
2367 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2369 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2370 /* ignore the APNG chunks */
2371 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2372 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2373 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2378 /* Define these outside of the following "if logging()" block so they will
2379 * show in debuggers.
2382 (void) ConcatenateMagickString(im_vers,
2383 MagickLibVersionText,32);
2384 (void) ConcatenateMagickString(im_vers,
2385 MagickLibAddendum,32);
2388 (void) ConcatenateMagickString(libpng_vers,
2389 PNG_LIBPNG_VER_STRING,32);
2391 (void) ConcatenateMagickString(libpng_runv,
2392 png_get_libpng_ver(NULL),32);
2395 (void) ConcatenateMagickString(zlib_vers,
2398 (void) ConcatenateMagickString(zlib_runv,
2401 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2402 " Enter ReadOnePNGImage()\n"
2403 " IM version = %s\n"
2404 " Libpng version = %s",
2405 im_vers, libpng_vers);
2407 if (logging != MagickFalse)
2409 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2411 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2414 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2416 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2418 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2423 #if (PNG_LIBPNG_VER < 10200)
2424 if (image_info->verbose)
2425 printf("Your PNG library (libpng-%s) is rather old.\n",
2426 PNG_LIBPNG_VER_STRING);
2429 #if (PNG_LIBPNG_VER >= 10400)
2430 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2431 if (image_info->verbose)
2433 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2434 PNG_LIBPNG_VER_STRING);
2435 printf("Please update it.\n");
2441 quantum_info = (QuantumInfo *) NULL;
2442 image=mng_info->image;
2444 if (logging != MagickFalse)
2446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2447 " Before reading:\n"
2448 " image->alpha_trait=%d"
2449 " image->rendering_intent=%d\n"
2450 " image->colorspace=%d\n"
2452 (int) image->alpha_trait, (int) image->rendering_intent,
2453 (int) image->colorspace, image->gamma);
2456 Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2458 /* Set to an out-of-range color unless tRNS chunk is present */
2459 transparent_color.red=65537;
2460 transparent_color.green=65537;
2461 transparent_color.blue=65537;
2462 transparent_color.alpha=65537;
2467 num_raw_profiles = 0;
2469 ping_found_cHRM = MagickFalse;
2470 ping_found_gAMA = MagickFalse;
2471 ping_found_iCCP = MagickFalse;
2472 ping_found_sRGB = MagickFalse;
2473 ping_found_sRGB_cHRM = MagickFalse;
2474 ping_preserve_iCCP = MagickFalse;
2478 Allocate the PNG structures
2480 #ifdef PNG_USER_MEM_SUPPORTED
2481 error_info.image=image;
2482 error_info.exception=exception;
2483 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2484 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2485 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2487 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2488 MagickPNGErrorHandler,MagickPNGWarningHandler);
2490 if (ping == (png_struct *) NULL)
2491 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2493 ping_info=png_create_info_struct(ping);
2495 if (ping_info == (png_info *) NULL)
2497 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2498 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2501 end_info=png_create_info_struct(ping);
2503 if (end_info == (png_info *) NULL)
2505 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2506 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2509 pixel_info=(MemoryInfo *) NULL;
2511 if (setjmp(png_jmpbuf(ping)))
2514 PNG image is corrupt.
2516 png_destroy_read_struct(&ping,&ping_info,&end_info);
2518 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2519 UnlockSemaphoreInfo(ping_semaphore);
2522 if (pixel_info != (MemoryInfo *) NULL)
2523 pixel_info=RelinquishVirtualMemory(pixel_info);
2525 if (logging != MagickFalse)
2526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2527 " exit ReadOnePNGImage() with error.");
2529 return(GetFirstImageInList(image));
2532 /* { For navigation to end of SETJMP-protected block. Within this
2533 * block, use png_error() instead of Throwing an Exception, to ensure
2534 * that libpng is able to clean up, and that the semaphore is unlocked.
2537 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2538 LockSemaphoreInfo(ping_semaphore);
2541 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2542 /* Allow benign errors */
2543 png_set_benign_errors(ping, 1);
2546 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2547 /* Reject images with too many rows or columns */
2548 png_set_user_limits(ping,
2549 (png_uint_32) MagickMin(0x7fffffffL,
2550 GetMagickResourceLimit(WidthResource)),
2551 (png_uint_32) MagickMin(0x7fffffffL,
2552 GetMagickResourceLimit(HeightResource)));
2553 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2556 Prepare PNG for reading.
2559 mng_info->image_found++;
2560 png_set_sig_bytes(ping,8);
2562 if (LocaleCompare(image_info->magick,"MNG") == 0)
2564 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2565 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2566 png_set_read_fn(ping,image,png_get_data);
2568 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2569 png_permit_empty_plte(ping,MagickTrue);
2570 png_set_read_fn(ping,image,png_get_data);
2572 mng_info->image=image;
2573 mng_info->bytes_in_read_buffer=0;
2574 mng_info->found_empty_plte=MagickFalse;
2575 mng_info->have_saved_bkgd_index=MagickFalse;
2576 png_set_read_fn(ping,mng_info,mng_get_data);
2582 png_set_read_fn(ping,image,png_get_data);
2588 value=GetImageOption(image_info,"profile:skip");
2590 if (IsOptionMember("ICC",value) == MagickFalse)
2593 value=GetImageOption(image_info,"png:preserve-iCCP");
2596 value=GetImageArtifact(image,"png:preserve-iCCP");
2599 ping_preserve_iCCP=MagickTrue;
2601 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2602 /* Don't let libpng check for ICC/sRGB profile because we're going
2603 * to do that anyway. This feature was added at libpng-1.6.12.
2604 * If logging, go ahead and check and issue a warning as appropriate.
2606 if (logging == MagickFalse)
2607 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2610 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2613 png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2617 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2618 /* Ignore unused chunks and all unknown chunks except for eXIf,
2619 zxIf, caNv, and vpAg */
2620 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2621 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2623 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2625 #if defined(zxIf_SUPPORTED)
2626 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_uxIf, 1);
2627 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_zxIf, 1);
2629 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_eXIf, 1);
2630 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2631 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2632 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2633 (int)sizeof(unused_chunks)/5);
2634 /* Callback for other unknown chunks */
2635 png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2638 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2639 # if (PNG_LIBPNG_VER >= 10400)
2640 /* Limit the size of the chunk storage cache used for sPLT, text,
2641 * and unknown chunks.
2643 png_set_chunk_cache_max(ping, 32767);
2647 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2648 /* Disable new libpng-1.5.10 feature */
2649 png_set_check_for_invalid_index (ping, 0);
2652 #if (PNG_LIBPNG_VER < 10400)
2653 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2654 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2655 /* Disable thread-unsafe features of pnggccrd */
2656 if (png_access_version_number() >= 10200)
2658 png_uint_32 mmx_disable_mask=0;
2659 png_uint_32 asm_flags;
2661 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2662 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2663 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2664 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2665 asm_flags=png_get_asm_flags(ping);
2666 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2671 png_read_info(ping,ping_info);
2673 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2674 &ping_bit_depth,&ping_color_type,
2675 &ping_interlace_method,&ping_compression_method,
2676 &ping_filter_method);
2678 ping_file_depth = ping_bit_depth;
2680 /* Swap bytes if requested */
2681 if (ping_file_depth == 16)
2686 value=GetImageOption(image_info,"png:swap-bytes");
2689 value=GetImageArtifact(image,"png:swap-bytes");
2695 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2698 msg[MagickPathExtent];
2700 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2701 (int) ping_color_type);
2702 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2704 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2705 (int) ping_bit_depth);
2706 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2709 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2712 (void) png_get_bKGD(ping, ping_info, &ping_background);
2714 if (ping_bit_depth < 8)
2716 png_set_packing(ping);
2720 image->depth=ping_bit_depth;
2721 image->depth=GetImageQuantumDepth(image,MagickFalse);
2722 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2724 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2725 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2727 image->rendering_intent=UndefinedIntent;
2728 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2729 (void) ResetMagickMemory(&image->chromaticity,0,
2730 sizeof(image->chromaticity));
2733 if (logging != MagickFalse)
2735 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2736 " PNG width: %.20g, height: %.20g\n"
2737 " PNG color_type: %d, bit_depth: %d\n"
2738 " PNG compression_method: %d\n"
2739 " PNG interlace_method: %d, filter_method: %d",
2740 (double) ping_width, (double) ping_height,
2741 ping_color_type, ping_bit_depth,
2742 ping_compression_method,
2743 ping_interlace_method,ping_filter_method);
2747 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2749 ping_found_iCCP=MagickTrue;
2750 if (logging != MagickFalse)
2751 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2752 " Found PNG iCCP chunk.");
2755 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2757 ping_found_gAMA=MagickTrue;
2758 if (logging != MagickFalse)
2759 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2760 " Found PNG gAMA chunk.");
2763 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2765 ping_found_cHRM=MagickTrue;
2766 if (logging != MagickFalse)
2767 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2768 " Found PNG cHRM chunk.");
2771 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2774 ping_found_sRGB=MagickTrue;
2775 if (logging != MagickFalse)
2776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2777 " Found PNG sRGB chunk.");
2780 #ifdef PNG_READ_iCCP_SUPPORTED
2781 if (ping_found_iCCP !=MagickTrue &&
2782 ping_found_sRGB != MagickTrue &&
2783 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2785 ping_found_iCCP=MagickTrue;
2786 if (logging != MagickFalse)
2787 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2788 " Found PNG iCCP chunk.");
2791 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2796 #if (PNG_LIBPNG_VER < 10500)
2810 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2813 if (profile_length != 0)
2818 if (logging != MagickFalse)
2819 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2820 " Reading PNG iCCP chunk.");
2822 profile=BlobToStringInfo(info,profile_length);
2824 if (profile == (StringInfo *) NULL)
2826 png_warning(ping, "ICC profile is NULL");
2827 profile=DestroyStringInfo(profile);
2831 if (ping_preserve_iCCP == MagickFalse)
2845 length=(png_uint_32) GetStringInfoLength(profile);
2847 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2849 if (length == sRGB_info[icheck].len)
2853 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2854 " Got a %lu-byte ICC profile (potentially sRGB)",
2855 (unsigned long) length);
2857 data=GetStringInfoDatum(profile);
2858 profile_crc=crc32(0,data,length);
2860 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2861 " with crc=%8x",(unsigned int) profile_crc);
2865 if (profile_crc == sRGB_info[icheck].crc)
2867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2868 " It is sRGB with rendering intent = %s",
2869 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2870 sRGB_info[icheck].intent));
2871 if (image->rendering_intent==UndefinedIntent)
2873 image->rendering_intent=
2874 Magick_RenderingIntent_from_PNG_RenderingIntent(
2875 sRGB_info[icheck].intent);
2881 if (sRGB_info[icheck].len == 0)
2883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2884 " Got %lu-byte ICC profile not recognized as sRGB",
2885 (unsigned long) length);
2886 (void) SetImageProfile(image,"icc",profile,exception);
2889 else /* Preserve-iCCP */
2891 (void) SetImageProfile(image,"icc",profile,exception);
2894 profile=DestroyStringInfo(profile);
2900 #if defined(PNG_READ_sRGB_SUPPORTED)
2902 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2905 if (png_get_sRGB(ping,ping_info,&intent))
2907 if (image->rendering_intent == UndefinedIntent)
2908 image->rendering_intent=
2909 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2911 if (logging != MagickFalse)
2912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2913 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2917 else if (mng_info->have_global_srgb)
2919 if (image->rendering_intent == UndefinedIntent)
2920 image->rendering_intent=
2921 Magick_RenderingIntent_from_PNG_RenderingIntent
2922 (mng_info->global_srgb_intent);
2929 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2930 if (mng_info->have_global_gama)
2931 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2933 if (png_get_gAMA(ping,ping_info,&file_gamma))
2935 image->gamma=(float) file_gamma;
2936 if (logging != MagickFalse)
2937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2938 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2942 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2944 if (mng_info->have_global_chrm != MagickFalse)
2946 (void) png_set_cHRM(ping,ping_info,
2947 mng_info->global_chrm.white_point.x,
2948 mng_info->global_chrm.white_point.y,
2949 mng_info->global_chrm.red_primary.x,
2950 mng_info->global_chrm.red_primary.y,
2951 mng_info->global_chrm.green_primary.x,
2952 mng_info->global_chrm.green_primary.y,
2953 mng_info->global_chrm.blue_primary.x,
2954 mng_info->global_chrm.blue_primary.y);
2958 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2960 (void) png_get_cHRM(ping,ping_info,
2961 &image->chromaticity.white_point.x,
2962 &image->chromaticity.white_point.y,
2963 &image->chromaticity.red_primary.x,
2964 &image->chromaticity.red_primary.y,
2965 &image->chromaticity.green_primary.x,
2966 &image->chromaticity.green_primary.y,
2967 &image->chromaticity.blue_primary.x,
2968 &image->chromaticity.blue_primary.y);
2970 ping_found_cHRM=MagickTrue;
2972 if (image->chromaticity.red_primary.x>0.6399f &&
2973 image->chromaticity.red_primary.x<0.6401f &&
2974 image->chromaticity.red_primary.y>0.3299f &&
2975 image->chromaticity.red_primary.y<0.3301f &&
2976 image->chromaticity.green_primary.x>0.2999f &&
2977 image->chromaticity.green_primary.x<0.3001f &&
2978 image->chromaticity.green_primary.y>0.5999f &&
2979 image->chromaticity.green_primary.y<0.6001f &&
2980 image->chromaticity.blue_primary.x>0.1499f &&
2981 image->chromaticity.blue_primary.x<0.1501f &&
2982 image->chromaticity.blue_primary.y>0.0599f &&
2983 image->chromaticity.blue_primary.y<0.0601f &&
2984 image->chromaticity.white_point.x>0.3126f &&
2985 image->chromaticity.white_point.x<0.3128f &&
2986 image->chromaticity.white_point.y>0.3289f &&
2987 image->chromaticity.white_point.y<0.3291f)
2988 ping_found_sRGB_cHRM=MagickTrue;
2991 if (image->rendering_intent != UndefinedIntent)
2993 if (ping_found_sRGB != MagickTrue &&
2994 (ping_found_gAMA != MagickTrue ||
2995 (image->gamma > .45 && image->gamma < .46)) &&
2996 (ping_found_cHRM != MagickTrue ||
2997 ping_found_sRGB_cHRM != MagickFalse) &&
2998 ping_found_iCCP != MagickTrue)
3000 png_set_sRGB(ping,ping_info,
3001 Magick_RenderingIntent_to_PNG_RenderingIntent
3002 (image->rendering_intent));
3003 file_gamma=1.000f/2.200f;
3004 ping_found_sRGB=MagickTrue;
3005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3006 " Setting sRGB as if in input");
3010 #if defined(PNG_oFFs_SUPPORTED)
3011 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3013 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
3014 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
3016 if (logging != MagickFalse)
3017 if (image->page.x || image->page.y)
3018 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3019 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
3020 image->page.x,(double) image->page.y);
3023 #if defined(PNG_pHYs_SUPPORTED)
3024 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3026 if (mng_info->have_global_phys)
3028 png_set_pHYs(ping,ping_info,
3029 mng_info->global_x_pixels_per_unit,
3030 mng_info->global_y_pixels_per_unit,
3031 mng_info->global_phys_unit_type);
3038 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3041 Set image resolution.
3043 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
3045 image->resolution.x=(double) x_resolution;
3046 image->resolution.y=(double) y_resolution;
3048 if (unit_type == PNG_RESOLUTION_METER)
3050 image->units=PixelsPerCentimeterResolution;
3051 image->resolution.x=(double) x_resolution/100.0;
3052 image->resolution.y=(double) y_resolution/100.0;
3055 if (logging != MagickFalse)
3056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3057 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
3058 (double) x_resolution,(double) y_resolution,unit_type);
3062 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3067 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3069 if ((number_colors == 0) &&
3070 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
3072 if (mng_info->global_plte_length)
3074 png_set_PLTE(ping,ping_info,mng_info->global_plte,
3075 (int) mng_info->global_plte_length);
3077 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3079 if (mng_info->global_trns_length)
3082 "global tRNS has more entries than global PLTE");
3086 png_set_tRNS(ping,ping_info,mng_info->global_trns,
3087 (int) mng_info->global_trns_length,NULL);
3090 #ifdef PNG_READ_bKGD_SUPPORTED
3092 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3093 mng_info->have_saved_bkgd_index ||
3095 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3100 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3101 if (mng_info->have_saved_bkgd_index)
3102 background.index=mng_info->saved_bkgd_index;
3104 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3105 background.index=ping_background->index;
3107 background.red=(png_uint_16)
3108 mng_info->global_plte[background.index].red;
3110 background.green=(png_uint_16)
3111 mng_info->global_plte[background.index].green;
3113 background.blue=(png_uint_16)
3114 mng_info->global_plte[background.index].blue;
3116 background.gray=(png_uint_16)
3117 mng_info->global_plte[background.index].green;
3119 png_set_bKGD(ping,ping_info,&background);
3124 png_error(ping,"No global PLTE in file");
3128 #ifdef PNG_READ_bKGD_SUPPORTED
3129 if (mng_info->have_global_bkgd &&
3130 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3131 image->background_color=mng_info->mng_global_bkgd;
3133 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3138 /* Set image background color.
3139 * Scale background components to 16-bit, then scale
3145 if (ping_file_depth == 1)
3148 else if (ping_file_depth == 2)
3151 else if (ping_file_depth == 4)
3154 if (ping_file_depth <= 8)
3157 ping_background->red *= bkgd_scale;
3158 ping_background->green *= bkgd_scale;
3159 ping_background->blue *= bkgd_scale;
3161 if (logging != MagickFalse)
3163 if (logging != MagickFalse)
3164 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3165 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3166 " bkgd_scale=%d. ping_background=(%d,%d,%d)",
3167 ping_background->red,ping_background->green,
3168 ping_background->blue,
3169 bkgd_scale,ping_background->red,
3170 ping_background->green,ping_background->blue);
3173 image->background_color.red=
3174 ScaleShortToQuantum(ping_background->red);
3176 image->background_color.green=
3177 ScaleShortToQuantum(ping_background->green);
3179 image->background_color.blue=
3180 ScaleShortToQuantum(ping_background->blue);
3182 image->background_color.alpha=OpaqueAlpha;
3184 if (logging != MagickFalse)
3185 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3186 " image->background_color=(%.20g,%.20g,%.20g).",
3187 (double) image->background_color.red,
3188 (double) image->background_color.green,
3189 (double) image->background_color.blue);
3191 #endif /* PNG_READ_bKGD_SUPPORTED */
3193 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3196 Image has a tRNS chunk.
3204 if (logging != MagickFalse)
3205 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3206 " Reading PNG tRNS chunk.");
3208 max_sample = (int) ((one << ping_file_depth) - 1);
3210 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3211 (int)ping_trans_color->gray > max_sample) ||
3212 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3213 ((int)ping_trans_color->red > max_sample ||
3214 (int)ping_trans_color->green > max_sample ||
3215 (int)ping_trans_color->blue > max_sample)))
3217 if (logging != MagickFalse)
3218 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3219 " Ignoring PNG tRNS chunk with out-of-range sample.");
3220 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3221 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3222 image->alpha_trait=UndefinedPixelTrait;
3229 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3231 /* Scale transparent_color to short */
3232 transparent_color.red= scale_to_short*ping_trans_color->red;
3233 transparent_color.green= scale_to_short*ping_trans_color->green;
3234 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3235 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3237 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3239 if (logging != MagickFalse)
3241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3242 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3243 (int) ping_trans_color->gray,(int) transparent_color.alpha);
3246 transparent_color.red=transparent_color.alpha;
3247 transparent_color.green=transparent_color.alpha;
3248 transparent_color.blue=transparent_color.alpha;
3252 #if defined(PNG_READ_sBIT_SUPPORTED)
3253 if (mng_info->have_global_sbit)
3255 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3256 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3259 num_passes=png_set_interlace_handling(ping);
3261 png_read_update_info(ping,ping_info);
3263 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3266 Initialize image structure.
3268 mng_info->image_box.left=0;
3269 mng_info->image_box.right=(ssize_t) ping_width;
3270 mng_info->image_box.top=0;
3271 mng_info->image_box.bottom=(ssize_t) ping_height;
3272 if (mng_info->mng_type == 0)
3274 mng_info->mng_width=ping_width;
3275 mng_info->mng_height=ping_height;
3276 mng_info->frame=mng_info->image_box;
3277 mng_info->clip=mng_info->image_box;
3282 image->page.y=mng_info->y_off[mng_info->object_id];
3285 image->compression=ZipCompression;
3286 image->columns=ping_width;
3287 image->rows=ping_height;
3289 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3290 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3293 image_gamma = image->gamma;
3295 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3296 " image->gamma=%f",(float) image_gamma);
3298 if (image_gamma > 0.75)
3300 /* Set image->rendering_intent to Undefined,
3301 * image->colorspace to GRAY, and reset image->chromaticity.
3303 image->intensity = Rec709LuminancePixelIntensityMethod;
3304 SetImageColorspace(image,GRAYColorspace,exception);
3309 save_rendering_intent = image->rendering_intent;
3311 save_chromaticity = image->chromaticity;
3313 SetImageColorspace(image,GRAYColorspace,exception);
3314 image->rendering_intent = save_rendering_intent;
3315 image->chromaticity = save_chromaticity;
3318 image->gamma = image_gamma;
3321 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3322 " image->colorspace=%d",(int) image->colorspace);
3324 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3325 ((int) ping_bit_depth < 16 &&
3326 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3331 image->storage_class=PseudoClass;
3333 image->colors=one << ping_file_depth;
3334 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3335 if (image->colors > 256)
3338 if (image->colors > 65536L)
3339 image->colors=65536L;
3341 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3346 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3347 image->colors=(size_t) number_colors;
3349 if (logging != MagickFalse)
3350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3351 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3355 if (image->storage_class == PseudoClass)
3358 Initialize image colormap.
3360 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3361 png_error(ping,"Memory allocation failed");
3363 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3368 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3370 for (i=0; i < (ssize_t) number_colors; i++)
3372 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3373 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3374 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3377 for ( ; i < (ssize_t) image->colors; i++)
3379 image->colormap[i].red=0;
3380 image->colormap[i].green=0;
3381 image->colormap[i].blue=0;
3390 scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3392 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3393 scale = ScaleShortToQuantum(scale);
3396 for (i=0; i < (ssize_t) image->colors; i++)
3398 image->colormap[i].red=(Quantum) (i*scale);
3399 image->colormap[i].green=(Quantum) (i*scale);
3400 image->colormap[i].blue=(Quantum) (i*scale);
3405 /* Set some properties for reporting by "identify" */
3408 msg[MagickPathExtent];
3410 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3411 ping_interlace_method in value */
3413 (void) FormatLocaleString(msg,MagickPathExtent,
3414 "%d, %d",(int) ping_width, (int) ping_height);
3415 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3417 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3418 (int) ping_file_depth);
3419 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3421 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3422 (int) ping_color_type,
3423 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3424 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3426 if (ping_interlace_method == 0)
3428 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3429 (int) ping_interlace_method);
3431 else if (ping_interlace_method == 1)
3433 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3434 (int) ping_interlace_method);
3438 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3439 (int) ping_interlace_method);
3441 (void) SetImageProperty(image,"png:IHDR.interlace_method",
3444 if (number_colors != 0)
3446 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3447 (int) number_colors);
3448 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3452 #if defined(PNG_tIME_SUPPORTED)
3453 read_tIME_chunk(image,ping,ping_info,exception);
3458 Read image scanlines.
3460 if (image->delay != 0)
3461 mng_info->scenes_found++;
3463 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3464 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3465 (image_info->first_scene+image_info->number_scenes))))
3467 /* This happens later in non-ping decodes */
3468 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3469 image->storage_class=DirectClass;
3471 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3472 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3473 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3474 BlendPixelTrait : UndefinedPixelTrait;
3476 if (logging != MagickFalse)
3477 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3478 " Skipping PNG image data for scene %.20g",(double)
3479 mng_info->scenes_found-1);
3480 png_destroy_read_struct(&ping,&ping_info,&end_info);
3482 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3483 UnlockSemaphoreInfo(ping_semaphore);
3486 if (logging != MagickFalse)
3487 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3488 " exit ReadOnePNGImage().");
3493 if (logging != MagickFalse)
3494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3495 " Reading PNG IDAT chunk(s)");
3497 status=SetImageExtent(image,image->columns,image->rows,exception);
3498 if (status == MagickFalse)
3499 return(DestroyImageList(image));
3502 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3503 sizeof(*ping_pixels));
3505 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3507 if (pixel_info == (MemoryInfo *) NULL)
3508 png_error(ping,"Memory allocation failed");
3509 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3511 if (logging != MagickFalse)
3512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3513 " Converting PNG pixels to pixel packets");
3515 Convert PNG pixels to pixel packets.
3517 quantum_info=AcquireQuantumInfo(image_info,image);
3519 if (quantum_info == (QuantumInfo *) NULL)
3520 png_error(ping,"Failed to allocate quantum_info");
3522 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3527 found_transparent_pixel;
3529 found_transparent_pixel=MagickFalse;
3531 if (image->storage_class == DirectClass)
3533 for (pass=0; pass < num_passes; pass++)
3536 Convert image to DirectClass pixel packets.
3539 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3540 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3541 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3542 BlendPixelTrait : UndefinedPixelTrait;
3544 for (y=0; y < (ssize_t) image->rows; y++)
3547 row_offset=ping_rowbytes*y;
3552 png_read_row(ping,ping_pixels+row_offset,NULL);
3554 if (pass < num_passes-1)
3557 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3559 if (q == (Quantum *) NULL)
3562 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3563 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3564 GrayQuantum,ping_pixels+row_offset,exception);
3566 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3567 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3568 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3570 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3571 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3572 RGBAQuantum,ping_pixels+row_offset,exception);
3574 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3575 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3576 IndexQuantum,ping_pixels+row_offset,exception);
3578 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3579 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3580 RGBQuantum,ping_pixels+row_offset,exception);
3582 if (found_transparent_pixel == MagickFalse)
3584 /* Is there a transparent pixel in the row? */
3585 if (y== 0 && logging != MagickFalse)
3586 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3587 " Looking for cheap transparent pixel");
3589 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3591 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3592 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3593 (GetPixelAlpha(image,q) != OpaqueAlpha))
3595 if (logging != MagickFalse)
3596 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3599 found_transparent_pixel = MagickTrue;
3602 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3603 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3604 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3605 transparent_color.red &&
3606 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3607 transparent_color.green &&
3608 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3609 transparent_color.blue))
3611 if (logging != MagickFalse)
3612 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3614 found_transparent_pixel = MagickTrue;
3617 q+=GetPixelChannels(image);
3621 if (num_passes == 1)
3623 status=SetImageProgress(image,LoadImageTag,
3624 (MagickOffsetType) y, image->rows);
3626 if (status == MagickFalse)
3629 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3633 if (num_passes != 1)
3635 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3636 if (status == MagickFalse)
3642 else /* image->storage_class != DirectClass */
3644 for (pass=0; pass < num_passes; pass++)
3653 Convert grayscale image to PseudoClass pixel packets.
3655 if (logging != MagickFalse)
3656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3657 " Converting grayscale pixels to pixel packets");
3659 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3660 BlendPixelTrait : UndefinedPixelTrait;
3662 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3663 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3664 sizeof(*quantum_scanline));
3666 if (quantum_scanline == (Quantum *) NULL)
3667 png_error(ping,"Memory allocation failed");
3669 for (y=0; y < (ssize_t) image->rows; y++)
3675 row_offset=ping_rowbytes*y;
3680 png_read_row(ping,ping_pixels+row_offset,NULL);
3682 if (pass < num_passes-1)
3685 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3687 if (q == (Quantum *) NULL)
3690 p=ping_pixels+row_offset;
3693 switch (ping_bit_depth)
3698 if (ping_color_type == 4)
3699 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3703 alpha=ScaleCharToQuantum((unsigned char)*p++);
3705 SetPixelAlpha(image,alpha,q);
3707 if (alpha != OpaqueAlpha)
3708 found_transparent_pixel = MagickTrue;
3710 q+=GetPixelChannels(image);
3714 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3722 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3724 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3728 if (image->colors > 256)
3729 quantum=((*p++) << 8);
3735 *r=ScaleShortToQuantum(quantum);
3738 if (ping_color_type == 4)
3740 if (image->colors > 256)
3741 quantum=((*p++) << 8);
3747 alpha=ScaleShortToQuantum(quantum);
3748 SetPixelAlpha(image,alpha,q);
3750 if (alpha != OpaqueAlpha)
3751 found_transparent_pixel = MagickTrue;
3753 q+=GetPixelChannels(image);
3756 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3758 p++; /* strip low byte */
3760 if (ping_color_type == 4)
3762 SetPixelAlpha(image,*p++,q);
3764 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3765 found_transparent_pixel = MagickTrue;
3768 q+=GetPixelChannels(image);
3781 Transfer image scanline.
3785 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3787 if (q == (Quantum *) NULL)
3789 for (x=0; x < (ssize_t) image->columns; x++)
3791 SetPixelIndex(image,*r++,q);
3792 q+=GetPixelChannels(image);
3795 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3798 if (num_passes == 1)
3800 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3803 if (status == MagickFalse)
3808 if (num_passes != 1)
3810 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3812 if (status == MagickFalse)
3816 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3819 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3820 UndefinedPixelTrait;
3822 if (logging != MagickFalse)
3824 if (found_transparent_pixel != MagickFalse)
3825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3826 " Found transparent pixel");
3829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3830 " No transparent pixel was found");
3832 ping_color_type&=0x03;
3837 if (quantum_info != (QuantumInfo *) NULL)
3838 quantum_info=DestroyQuantumInfo(quantum_info);
3840 if (image->storage_class == PseudoClass)
3845 alpha_trait=image->alpha_trait;
3846 image->alpha_trait=UndefinedPixelTrait;
3847 (void) SyncImage(image,exception);
3848 image->alpha_trait=alpha_trait;
3851 png_read_end(ping,end_info);
3853 if (logging != MagickFalse)
3855 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3856 " image->storage_class=%d\n",(int) image->storage_class);
3859 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3860 (ssize_t) image_info->first_scene && image->delay != 0)
3862 png_destroy_read_struct(&ping,&ping_info,&end_info);
3863 pixel_info=RelinquishVirtualMemory(pixel_info);
3865 (void) SetImageBackgroundColor(image,exception);
3866 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3867 UnlockSemaphoreInfo(ping_semaphore);
3869 if (logging != MagickFalse)
3870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3871 " exit ReadOnePNGImage() early.");
3875 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3881 Image has a transparent background.
3883 storage_class=image->storage_class;
3884 image->alpha_trait=BlendPixelTrait;
3886 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3888 if (storage_class == PseudoClass)
3890 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3892 for (x=0; x < ping_num_trans; x++)
3894 image->colormap[x].alpha_trait=BlendPixelTrait;
3895 image->colormap[x].alpha =
3896 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3900 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3902 for (x=0; x < (int) image->colors; x++)
3904 if (ScaleQuantumToShort(image->colormap[x].red) ==
3905 transparent_color.alpha)
3907 image->colormap[x].alpha_trait=BlendPixelTrait;
3908 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3912 (void) SyncImage(image,exception);
3915 #if 1 /* Should have already been done above, but glennrp problem P10
3920 for (y=0; y < (ssize_t) image->rows; y++)
3922 image->storage_class=storage_class;
3923 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3925 if (q == (Quantum *) NULL)
3929 /* Caution: on a Q8 build, this does not distinguish between
3930 * 16-bit colors that differ only in the low byte
3932 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3934 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3935 transparent_color.red &&
3936 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3937 transparent_color.green &&
3938 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3939 transparent_color.blue)
3941 SetPixelAlpha(image,TransparentAlpha,q);
3944 #if 0 /* I have not found a case where this is needed. */
3947 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3951 q+=GetPixelChannels(image);
3954 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3960 image->storage_class=DirectClass;
3963 for (j = 0; j < 2; j++)
3966 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3967 MagickTrue : MagickFalse;
3969 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3970 MagickTrue : MagickFalse;
3972 if (status != MagickFalse)
3973 for (i=0; i < (ssize_t) num_text; i++)
3975 /* Check for a profile */
3977 if (logging != MagickFalse)
3978 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3979 " Reading PNG text chunk");
3981 if (strlen(text[i].key) > 16 &&
3982 memcmp(text[i].key, "Raw profile type ",17) == 0)
3987 value=GetImageOption(image_info,"profile:skip");
3989 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3991 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3994 if (logging != MagickFalse)
3995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3996 " Read raw profile %s",text[i].key+17);
4000 if (logging != MagickFalse)
4001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4002 " Skipping raw profile %s",text[i].key+17);
4011 length=text[i].text_length;
4012 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
4014 if (value == (char *) NULL)
4016 png_error(ping,"Memory allocation failed");
4020 (void) ConcatenateMagickString(value,text[i].text,length+2);
4022 /* Don't save "density" or "units" property if we have a pHYs
4025 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
4026 (LocaleCompare(text[i].key,"density") != 0 &&
4027 LocaleCompare(text[i].key,"units") != 0))
4028 (void) SetImageProperty(image,text[i].key,value,exception);
4030 if (logging != MagickFalse)
4032 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4035 (unsigned long) length,
4039 value=DestroyString(value);
4042 num_text_total += num_text;
4045 #ifdef MNG_OBJECT_BUFFERS
4047 Store the object if necessary.
4049 if (object_id && !mng_info->frozen[object_id])
4051 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4054 create a new object buffer.
4056 mng_info->ob[object_id]=(MngBuffer *)
4057 AcquireMagickMemory(sizeof(MngBuffer));
4059 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
4061 mng_info->ob[object_id]->image=(Image *) NULL;
4062 mng_info->ob[object_id]->reference_count=1;
4066 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
4067 mng_info->ob[object_id]->frozen)
4069 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4070 png_error(ping,"Memory allocation failed");
4072 if (mng_info->ob[object_id]->frozen)
4073 png_error(ping,"Cannot overwrite frozen MNG object buffer");
4079 if (mng_info->ob[object_id]->image != (Image *) NULL)
4080 mng_info->ob[object_id]->image=DestroyImage
4081 (mng_info->ob[object_id]->image);
4083 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4086 if (mng_info->ob[object_id]->image != (Image *) NULL)
4087 mng_info->ob[object_id]->image->file=(FILE *) NULL;
4090 png_error(ping, "Cloning image for object buffer failed");
4092 if (ping_width > 250000L || ping_height > 250000L)
4093 png_error(ping,"PNG Image dimensions are too large.");
4095 mng_info->ob[object_id]->width=ping_width;
4096 mng_info->ob[object_id]->height=ping_height;
4097 mng_info->ob[object_id]->color_type=ping_color_type;
4098 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4099 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4100 mng_info->ob[object_id]->compression_method=
4101 ping_compression_method;
4102 mng_info->ob[object_id]->filter_method=ping_filter_method;
4104 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4110 Copy the PLTE to the object buffer.
4112 png_get_PLTE(ping,ping_info,&plte,&number_colors);
4113 mng_info->ob[object_id]->plte_length=number_colors;
4115 for (i=0; i < number_colors; i++)
4117 mng_info->ob[object_id]->plte[i]=plte[i];
4122 mng_info->ob[object_id]->plte_length=0;
4127 /* Set image->alpha_trait to MagickTrue if the input colortype supports
4128 * alpha or if a valid tRNS chunk is present, no matter whether there
4129 * is actual transparency present.
4131 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4132 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4133 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4134 BlendPixelTrait : UndefinedPixelTrait;
4136 #if 0 /* I'm not sure what's wrong here but it does not work. */
4137 if (image->alpha_trait != UndefinedPixelTrait)
4139 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4140 (void) SetImageType(image,GrayscaleAlphaType,exception);
4142 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4143 (void) SetImageType(image,PaletteAlphaType,exception);
4146 (void) SetImageType(image,TrueColorAlphaType,exception);
4151 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4152 (void) SetImageType(image,GrayscaleType,exception);
4154 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4155 (void) SetImageType(image,PaletteType,exception);
4158 (void) SetImageType(image,TrueColorType,exception);
4162 /* Set more properties for identify to retrieve */
4165 msg[MagickPathExtent];
4167 if (num_text_total != 0)
4169 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4170 (void) FormatLocaleString(msg,MagickPathExtent,
4171 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4172 (void) SetImageProperty(image,"png:text",msg,
4176 if (num_raw_profiles != 0)
4178 (void) FormatLocaleString(msg,MagickPathExtent,
4179 "%d were found", num_raw_profiles);
4180 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4185 if (ping_found_cHRM != MagickFalse)
4187 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4188 "chunk was found (see Chromaticity, above)");
4189 (void) SetImageProperty(image,"png:cHRM",msg,
4194 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4196 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4197 "chunk was found (see Background color, above)");
4198 (void) SetImageProperty(image,"png:bKGD",msg,
4202 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4205 #if defined(PNG_iCCP_SUPPORTED)
4207 if (ping_found_iCCP != MagickFalse)
4208 (void) SetImageProperty(image,"png:iCCP",msg,
4212 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4213 (void) SetImageProperty(image,"png:tRNS",msg,
4216 #if defined(PNG_sRGB_SUPPORTED)
4218 if (ping_found_sRGB != MagickFalse)
4220 (void) FormatLocaleString(msg,MagickPathExtent,
4223 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4224 (void) SetImageProperty(image,"png:sRGB",msg,
4230 if (ping_found_gAMA != MagickFalse)
4232 (void) FormatLocaleString(msg,MagickPathExtent,
4233 "gamma=%.8g (See Gamma, above)",
4235 (void) SetImageProperty(image,"png:gAMA",msg,
4239 #if defined(PNG_pHYs_SUPPORTED)
4241 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4243 (void) FormatLocaleString(msg,MagickPathExtent,
4244 "x_res=%.10g, y_res=%.10g, units=%d",
4245 (double) x_resolution,(double) y_resolution, unit_type);
4246 (void) SetImageProperty(image,"png:pHYs",msg,
4251 #if defined(PNG_oFFs_SUPPORTED)
4253 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4255 (void) FormatLocaleString(msg,MagickPathExtent,
4256 "x_off=%.20g, y_off=%.20g",
4257 (double) image->page.x,(double) image->page.y);
4258 (void) SetImageProperty(image,"png:oFFs",msg,
4263 #if defined(PNG_tIME_SUPPORTED)
4264 read_tIME_chunk(image,ping,end_info,exception);
4268 if ((image->page.width != 0 && image->page.width != image->columns) ||
4269 (image->page.height != 0 && image->page.height != image->rows) ||
4270 (image->page.x != 0 || image->page.y != 0))
4272 (void) FormatLocaleString(msg,MagickPathExtent,
4273 "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4274 (double) image->page.width,(double) image->page.height,
4275 (double) image->page.x,(double) image->page.y);
4276 (void) SetImageProperty(image,"png:caNv",msg,
4281 if ((image->page.width != 0 && image->page.width != image->columns) ||
4282 (image->page.height != 0 && image->page.height != image->rows))
4284 (void) FormatLocaleString(msg,MagickPathExtent,
4285 "width=%.20g, height=%.20g",
4286 (double) image->page.width,(double) image->page.height);
4287 (void) SetImageProperty(image,"png:vpAg",msg,
4293 Relinquish resources.
4295 png_destroy_read_struct(&ping,&ping_info,&end_info);
4297 pixel_info=RelinquishVirtualMemory(pixel_info);
4299 if (logging != MagickFalse)
4300 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4301 " exit ReadOnePNGImage()");
4303 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4304 UnlockSemaphoreInfo(ping_semaphore);
4307 /* } for navigation to beginning of SETJMP-protected block, revert to
4308 * Throwing an Exception when an error occurs.
4313 /* end of reading one PNG image */
4316 static Image *ReadPNGImage(const ImageInfo *image_info,
4317 ExceptionInfo *exception)
4331 magic_number[MagickPathExtent];
4339 assert(image_info != (const ImageInfo *) NULL);
4340 assert(image_info->signature == MagickCoreSignature);
4342 if (image_info->debug != MagickFalse)
4343 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4344 image_info->filename);
4346 assert(exception != (ExceptionInfo *) NULL);
4347 assert(exception->signature == MagickCoreSignature);
4348 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4349 image=AcquireImage(image_info,exception);
4350 mng_info=(MngInfo *) NULL;
4351 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4353 if (status == MagickFalse)
4354 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4357 Verify PNG signature.
4359 count=ReadBlob(image,8,(unsigned char *) magic_number);
4361 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4362 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4365 Allocate a MngInfo structure.
4367 have_mng_structure=MagickFalse;
4368 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4370 if (mng_info == (MngInfo *) NULL)
4371 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4374 Initialize members of the MngInfo structure.
4376 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4377 mng_info->image=image;
4378 have_mng_structure=MagickTrue;
4380 image=ReadOnePNGImage(mng_info,image_info,exception);
4381 MngInfoFreeStruct(mng_info,&have_mng_structure);
4383 if (image == (Image *) NULL)
4385 if (logging != MagickFalse)
4386 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4387 "exit ReadPNGImage() with error");
4389 return((Image *) NULL);
4392 (void) CloseBlob(image);
4394 if ((image->columns == 0) || (image->rows == 0))
4396 if (logging != MagickFalse)
4397 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4398 "exit ReadPNGImage() with error.");
4400 ThrowReaderException(CorruptImageError,"CorruptImage");
4403 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4404 ((image->gamma < .45) || (image->gamma > .46)) &&
4405 !(image->chromaticity.red_primary.x>0.6399f &&
4406 image->chromaticity.red_primary.x<0.6401f &&
4407 image->chromaticity.red_primary.y>0.3299f &&
4408 image->chromaticity.red_primary.y<0.3301f &&
4409 image->chromaticity.green_primary.x>0.2999f &&
4410 image->chromaticity.green_primary.x<0.3001f &&
4411 image->chromaticity.green_primary.y>0.5999f &&
4412 image->chromaticity.green_primary.y<0.6001f &&
4413 image->chromaticity.blue_primary.x>0.1499f &&
4414 image->chromaticity.blue_primary.x<0.1501f &&
4415 image->chromaticity.blue_primary.y>0.0599f &&
4416 image->chromaticity.blue_primary.y<0.0601f &&
4417 image->chromaticity.white_point.x>0.3126f &&
4418 image->chromaticity.white_point.x<0.3128f &&
4419 image->chromaticity.white_point.y>0.3289f &&
4420 image->chromaticity.white_point.y<0.3291f))
4422 SetImageColorspace(image,RGBColorspace,exception);
4425 if (logging != MagickFalse)
4427 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4428 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4429 (double) image->page.width,(double) image->page.height,
4430 (double) image->page.x,(double) image->page.y);
4431 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4432 " image->colorspace: %d", (int) image->colorspace);
4435 if (logging != MagickFalse)
4436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4443 #if defined(JNG_SUPPORTED)
4445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4449 % R e a d O n e J N G I m a g e %
4453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4455 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4456 % (minus the 8-byte signature) and returns it. It allocates the memory
4457 % necessary for the new Image structure and returns a pointer to the new
4460 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4462 % The format of the ReadOneJNGImage method is:
4464 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4465 % ExceptionInfo *exception)
4467 % A description of each parameter follows:
4469 % o mng_info: Specifies a pointer to a MngInfo structure.
4471 % o image_info: the image info.
4473 % o exception: return any errors or warnings in this structure.
4476 static Image *ReadOneJNGImage(MngInfo *mng_info,
4477 const ImageInfo *image_info, ExceptionInfo *exception)
4504 jng_image_sample_depth,
4505 jng_image_compression_method,
4506 jng_image_interlace_method,
4507 jng_alpha_sample_depth,
4508 jng_alpha_compression_method,
4509 jng_alpha_filter_method,
4510 jng_alpha_interlace_method;
4512 register const Quantum
4522 register unsigned char
4532 jng_alpha_compression_method=0;
4533 jng_alpha_sample_depth=8;
4537 alpha_image=(Image *) NULL;
4538 color_image=(Image *) NULL;
4539 alpha_image_info=(ImageInfo *) NULL;
4540 color_image_info=(ImageInfo *) NULL;
4542 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4543 " Enter ReadOneJNGImage()");
4545 image=mng_info->image;
4547 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4550 Allocate next image structure.
4552 if (logging != MagickFalse)
4553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4554 " AcquireNextImage()");
4556 AcquireNextImage(image_info,image,exception);
4558 if (GetNextImageInList(image) == (Image *) NULL)
4559 return((Image *) NULL);
4561 image=SyncNextImageInList(image);
4563 mng_info->image=image;
4566 Signature bytes have already been read.
4569 read_JSEP=MagickFalse;
4570 reading_idat=MagickFalse;
4574 type[MagickPathExtent];
4583 Read a new JNG chunk.
4585 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4586 2*GetBlobSize(image));
4588 if (status == MagickFalse)
4592 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4593 length=ReadBlobMSBLong(image);
4594 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4596 if (logging != MagickFalse)
4597 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4598 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4599 type[0],type[1],type[2],type[3],(double) length);
4601 if (length > PNG_UINT_31_MAX || count == 0)
4602 ThrowReaderException(CorruptImageError,"CorruptImage");
4605 chunk=(unsigned char *) NULL;
4609 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4611 if (chunk == (unsigned char *) NULL)
4612 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4614 for (i=0; i < (ssize_t) length; i++)
4615 chunk[i]=(unsigned char) ReadBlobByte(image);
4620 (void) ReadBlobMSBLong(image); /* read crc word */
4622 if (memcmp(type,mng_JHDR,4) == 0)
4626 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4627 (p[2] << 8) | p[3]);
4628 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4629 (p[6] << 8) | p[7]);
4630 if ((jng_width == 0) || (jng_height == 0))
4631 ThrowReaderException(CorruptImageError,
4632 "NegativeOrZeroImageSize");
4633 jng_color_type=p[8];
4634 jng_image_sample_depth=p[9];
4635 jng_image_compression_method=p[10];
4636 jng_image_interlace_method=p[11];
4638 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4641 jng_alpha_sample_depth=p[12];
4642 jng_alpha_compression_method=p[13];
4643 jng_alpha_filter_method=p[14];
4644 jng_alpha_interlace_method=p[15];
4646 if (logging != MagickFalse)
4648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4649 " jng_width: %16lu, jng_height: %16lu\n"
4650 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4651 " jng_image_compression_method:%3d",
4652 (unsigned long) jng_width, (unsigned long) jng_height,
4653 jng_color_type, jng_image_sample_depth,
4654 jng_image_compression_method);
4656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4657 " jng_image_interlace_method: %3d"
4658 " jng_alpha_sample_depth: %3d",
4659 jng_image_interlace_method,
4660 jng_alpha_sample_depth);
4662 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4663 " jng_alpha_compression_method:%3d\n"
4664 " jng_alpha_filter_method: %3d\n"
4665 " jng_alpha_interlace_method: %3d",
4666 jng_alpha_compression_method,
4667 jng_alpha_filter_method,
4668 jng_alpha_interlace_method);
4673 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4679 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4680 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4681 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4684 o create color_image
4685 o open color_blob, attached to color_image
4686 o if (color type has alpha)
4687 open alpha_blob, attached to alpha_image
4690 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4692 if (color_image_info == (ImageInfo *) NULL)
4693 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4695 GetImageInfo(color_image_info);
4696 color_image=AcquireImage(color_image_info,exception);
4698 if (color_image == (Image *) NULL)
4699 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4701 if (logging != MagickFalse)
4702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4703 " Creating color_blob.");
4705 (void) AcquireUniqueFilename(color_image->filename);
4706 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4709 if (status == MagickFalse)
4710 return((Image *) NULL);
4712 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4714 alpha_image_info=(ImageInfo *)
4715 AcquireMagickMemory(sizeof(ImageInfo));
4717 if (alpha_image_info == (ImageInfo *) NULL)
4718 ThrowReaderException(ResourceLimitError,
4719 "MemoryAllocationFailed");
4721 GetImageInfo(alpha_image_info);
4722 alpha_image=AcquireImage(alpha_image_info,exception);
4724 if (alpha_image == (Image *) NULL)
4726 alpha_image=DestroyImage(alpha_image);
4727 ThrowReaderException(ResourceLimitError,
4728 "MemoryAllocationFailed");
4731 if (logging != MagickFalse)
4732 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4733 " Creating alpha_blob.");
4735 (void) AcquireUniqueFilename(alpha_image->filename);
4736 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4739 if (status == MagickFalse)
4740 return((Image *) NULL);
4742 if (jng_alpha_compression_method == 0)
4747 if (logging != MagickFalse)
4748 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4749 " Writing IHDR chunk to alpha_blob.");
4751 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4752 "\211PNG\r\n\032\n");
4754 (void) WriteBlobMSBULong(alpha_image,13L);
4755 PNGType(data,mng_IHDR);
4756 LogPNGChunk(logging,mng_IHDR,13L);
4757 PNGLong(data+4,jng_width);
4758 PNGLong(data+8,jng_height);
4759 data[12]=jng_alpha_sample_depth;
4760 data[13]=0; /* color_type gray */
4761 data[14]=0; /* compression method 0 */
4762 data[15]=0; /* filter_method 0 */
4763 data[16]=0; /* interlace_method 0 */
4764 (void) WriteBlob(alpha_image,17,data);
4765 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4768 reading_idat=MagickTrue;
4771 if (memcmp(type,mng_JDAT,4) == 0)
4773 /* Copy chunk to color_image->blob */
4775 if (logging != MagickFalse)
4776 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4777 " Copying JDAT chunk data to color_blob.");
4779 (void) WriteBlob(color_image,length,chunk);
4782 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4787 if (memcmp(type,mng_IDAT,4) == 0)
4792 /* Copy IDAT header and chunk data to alpha_image->blob */
4794 if (alpha_image != NULL && image_info->ping == MagickFalse)
4796 if (logging != MagickFalse)
4797 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4798 " Copying IDAT chunk data to alpha_blob.");
4800 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4801 PNGType(data,mng_IDAT);
4802 LogPNGChunk(logging,mng_IDAT,length);
4803 (void) WriteBlob(alpha_image,4,data);
4804 (void) WriteBlob(alpha_image,length,chunk);
4805 (void) WriteBlobMSBULong(alpha_image,
4806 crc32(crc32(0,data,4),chunk,(uInt) length));
4810 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4815 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4817 /* Copy chunk data to alpha_image->blob */
4819 if (alpha_image != NULL && image_info->ping == MagickFalse)
4821 if (logging != MagickFalse)
4822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4823 " Copying JDAA chunk data to alpha_blob.");
4825 (void) WriteBlob(alpha_image,length,chunk);
4829 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4834 if (memcmp(type,mng_JSEP,4) == 0)
4836 read_JSEP=MagickTrue;
4839 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4844 if (memcmp(type,mng_bKGD,4) == 0)
4848 image->background_color.red=ScaleCharToQuantum(p[1]);
4849 image->background_color.green=image->background_color.red;
4850 image->background_color.blue=image->background_color.red;
4855 image->background_color.red=ScaleCharToQuantum(p[1]);
4856 image->background_color.green=ScaleCharToQuantum(p[3]);
4857 image->background_color.blue=ScaleCharToQuantum(p[5]);
4860 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4864 if (memcmp(type,mng_gAMA,4) == 0)
4867 image->gamma=((float) mng_get_long(p))*0.00001;
4869 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4873 if (memcmp(type,mng_cHRM,4) == 0)
4877 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4878 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4879 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4880 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4881 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4882 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4883 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4884 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4887 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4891 if (memcmp(type,mng_sRGB,4) == 0)
4895 image->rendering_intent=
4896 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4897 image->gamma=1.000f/2.200f;
4898 image->chromaticity.red_primary.x=0.6400f;
4899 image->chromaticity.red_primary.y=0.3300f;
4900 image->chromaticity.green_primary.x=0.3000f;
4901 image->chromaticity.green_primary.y=0.6000f;
4902 image->chromaticity.blue_primary.x=0.1500f;
4903 image->chromaticity.blue_primary.y=0.0600f;
4904 image->chromaticity.white_point.x=0.3127f;
4905 image->chromaticity.white_point.y=0.3290f;
4908 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4912 if (memcmp(type,mng_oFFs,4) == 0)
4916 image->page.x=(ssize_t) mng_get_long(p);
4917 image->page.y=(ssize_t) mng_get_long(&p[4]);
4919 if ((int) p[8] != 0)
4921 image->page.x/=10000;
4922 image->page.y/=10000;
4927 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4932 if (memcmp(type,mng_pHYs,4) == 0)
4936 image->resolution.x=(double) mng_get_long(p);
4937 image->resolution.y=(double) mng_get_long(&p[4]);
4938 if ((int) p[8] == PNG_RESOLUTION_METER)
4940 image->units=PixelsPerCentimeterResolution;
4941 image->resolution.x=image->resolution.x/100.0f;
4942 image->resolution.y=image->resolution.y/100.0f;
4946 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4951 if (memcmp(type,mng_iCCP,4) == 0)
4955 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4962 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4964 if (memcmp(type,mng_IEND,4))
4974 Finish up reading image data:
4976 o read main image from color_blob.
4980 o if (color_type has alpha)
4981 if alpha_encoding is PNG
4982 read secondary image from alpha_blob via ReadPNG
4983 if alpha_encoding is JPEG
4984 read secondary image from alpha_blob via ReadJPEG
4988 o copy intensity of secondary image into
4989 alpha samples of main image.
4991 o destroy the secondary image.
4994 if (color_image_info == (ImageInfo *) NULL)
4996 assert(color_image == (Image *) NULL);
4997 assert(alpha_image == (Image *) NULL);
4998 return((Image *) NULL);
5001 if (color_image == (Image *) NULL)
5003 assert(alpha_image == (Image *) NULL);
5004 return((Image *) NULL);
5007 (void) SeekBlob(color_image,0,SEEK_SET);
5009 if (logging != MagickFalse)
5010 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5011 " Reading jng_image from color_blob.");
5013 assert(color_image_info != (ImageInfo *) NULL);
5014 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
5015 color_image->filename);
5017 color_image_info->ping=MagickFalse; /* To do: avoid this */
5018 jng_image=ReadImage(color_image_info,exception);
5020 (void) RelinquishUniqueFileResource(color_image->filename);
5021 color_image=DestroyImage(color_image);
5022 color_image_info=DestroyImageInfo(color_image_info);
5024 if (jng_image == (Image *) NULL)
5025 return((Image *) NULL);
5027 if (logging != MagickFalse)
5028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5029 " Copying jng_image pixels to main image.");
5031 image->rows=jng_height;
5032 image->columns=jng_width;
5034 status=SetImageExtent(image,image->columns,image->rows,exception);
5035 if (status == MagickFalse)
5036 return(DestroyImageList(image));
5038 for (y=0; y < (ssize_t) image->rows; y++)
5040 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5041 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5042 for (x=(ssize_t) image->columns; x != 0; x--)
5044 SetPixelRed(image,GetPixelRed(jng_image,s),q);
5045 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5046 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5047 q+=GetPixelChannels(image);
5048 s+=GetPixelChannels(jng_image);
5051 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5055 jng_image=DestroyImage(jng_image);
5057 if (image_info->ping == MagickFalse)
5059 if (jng_color_type >= 12)
5061 if (jng_alpha_compression_method == 0)
5065 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5066 PNGType(data,mng_IEND);
5067 LogPNGChunk(logging,mng_IEND,0L);
5068 (void) WriteBlob(alpha_image,4,data);
5069 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5072 (void) CloseBlob(alpha_image);
5074 if (logging != MagickFalse)
5075 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5076 " Reading alpha from alpha_blob.");
5078 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5079 "%s",alpha_image->filename);
5081 jng_image=ReadImage(alpha_image_info,exception);
5083 if (jng_image != (Image *) NULL)
5084 for (y=0; y < (ssize_t) image->rows; y++)
5086 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
5088 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5090 if (image->alpha_trait != UndefinedPixelTrait)
5091 for (x=(ssize_t) image->columns; x != 0; x--)
5093 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5094 q+=GetPixelChannels(image);
5095 s+=GetPixelChannels(jng_image);
5099 for (x=(ssize_t) image->columns; x != 0; x--)
5101 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5102 if (GetPixelAlpha(image,q) != OpaqueAlpha)
5103 image->alpha_trait=BlendPixelTrait;
5104 q+=GetPixelChannels(image);
5105 s+=GetPixelChannels(jng_image);
5108 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5111 (void) RelinquishUniqueFileResource(alpha_image->filename);
5112 alpha_image=DestroyImage(alpha_image);
5113 alpha_image_info=DestroyImageInfo(alpha_image_info);
5114 if (jng_image != (Image *) NULL)
5115 jng_image=DestroyImage(jng_image);
5119 /* Read the JNG image. */
5121 if (mng_info->mng_type == 0)
5123 mng_info->mng_width=jng_width;
5124 mng_info->mng_height=jng_height;
5127 if (image->page.width == 0 && image->page.height == 0)
5129 image->page.width=jng_width;
5130 image->page.height=jng_height;
5133 if (image->page.x == 0 && image->page.y == 0)
5135 image->page.x=mng_info->x_off[mng_info->object_id];
5136 image->page.y=mng_info->y_off[mng_info->object_id];
5141 image->page.y=mng_info->y_off[mng_info->object_id];
5144 mng_info->image_found++;
5145 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5146 2*GetBlobSize(image));
5148 if (status == MagickFalse)
5149 return((Image *) NULL);
5151 if (logging != MagickFalse)
5152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5153 " exit ReadOneJNGImage()");
5159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5163 % R e a d J N G I m a g e %
5167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5169 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5170 % (including the 8-byte signature) and returns it. It allocates the memory
5171 % necessary for the new Image structure and returns a pointer to the new
5174 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
5176 % The format of the ReadJNGImage method is:
5178 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5181 % A description of each parameter follows:
5183 % o image_info: the image info.
5185 % o exception: return any errors or warnings in this structure.
5189 static Image *ReadJNGImage(const ImageInfo *image_info,
5190 ExceptionInfo *exception)
5204 magic_number[MagickPathExtent];
5212 assert(image_info != (const ImageInfo *) NULL);
5213 assert(image_info->signature == MagickCoreSignature);
5214 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5215 image_info->filename);
5216 assert(exception != (ExceptionInfo *) NULL);
5217 assert(exception->signature == MagickCoreSignature);
5218 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5219 image=AcquireImage(image_info,exception);
5220 mng_info=(MngInfo *) NULL;
5221 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5223 if (status == MagickFalse)
5224 return((Image *) NULL);
5226 if (LocaleCompare(image_info->magick,"JNG") != 0)
5227 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5229 /* Verify JNG signature. */
5231 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5233 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5234 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5236 /* Allocate a MngInfo structure. */
5238 have_mng_structure=MagickFalse;
5239 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5241 if (mng_info == (MngInfo *) NULL)
5242 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5244 /* Initialize members of the MngInfo structure. */
5246 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5247 have_mng_structure=MagickTrue;
5249 mng_info->image=image;
5250 image=ReadOneJNGImage(mng_info,image_info,exception);
5251 MngInfoFreeStruct(mng_info,&have_mng_structure);
5253 if (image == (Image *) NULL)
5255 if (logging != MagickFalse)
5256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5257 "exit ReadJNGImage() with error");
5259 return((Image *) NULL);
5261 (void) CloseBlob(image);
5263 if (image->columns == 0 || image->rows == 0)
5265 if (logging != MagickFalse)
5266 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5267 "exit ReadJNGImage() with error");
5269 ThrowReaderException(CorruptImageError,"CorruptImage");
5272 if (logging != MagickFalse)
5273 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5279 static Image *ReadMNGImage(const ImageInfo *image_info,
5280 ExceptionInfo *exception)
5283 page_geometry[MagickPathExtent];
5315 #if defined(MNG_INSERT_LAYERS)
5317 mng_background_color;
5320 register unsigned char
5335 #if defined(MNG_INSERT_LAYERS)
5340 volatile unsigned int
5341 #ifdef MNG_OBJECT_BUFFERS
5342 mng_background_object=0,
5344 mng_type=0; /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5347 default_frame_timeout,
5349 #if defined(MNG_INSERT_LAYERS)
5355 /* These delays are all measured in image ticks_per_second,
5356 * not in MNG ticks_per_second
5359 default_frame_delay,
5363 #if defined(MNG_INSERT_LAYERS)
5372 previous_fb.bottom=0;
5374 previous_fb.right=0;
5376 default_fb.bottom=0;
5380 /* Open image file. */
5382 assert(image_info != (const ImageInfo *) NULL);
5383 assert(image_info->signature == MagickCoreSignature);
5384 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5385 image_info->filename);
5386 assert(exception != (ExceptionInfo *) NULL);
5387 assert(exception->signature == MagickCoreSignature);
5388 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5389 image=AcquireImage(image_info,exception);
5390 mng_info=(MngInfo *) NULL;
5391 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5393 if (status == MagickFalse)
5394 return((Image *) NULL);
5396 first_mng_object=MagickFalse;
5398 have_mng_structure=MagickFalse;
5400 /* Allocate a MngInfo structure. */
5402 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5404 if (mng_info == (MngInfo *) NULL)
5405 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5407 /* Initialize members of the MngInfo structure. */
5409 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5410 mng_info->image=image;
5411 have_mng_structure=MagickTrue;
5413 if (LocaleCompare(image_info->magick,"MNG") == 0)
5416 magic_number[MagickPathExtent];
5418 /* Verify MNG signature. */
5419 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5420 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5421 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5423 /* Initialize some nonzero members of the MngInfo structure. */
5424 for (i=0; i < MNG_MAX_OBJECTS; i++)
5426 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5427 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5429 mng_info->exists[0]=MagickTrue;
5432 first_mng_object=MagickTrue;
5434 #if defined(MNG_INSERT_LAYERS)
5435 insert_layers=MagickFalse; /* should be False during convert or mogrify */
5437 default_frame_delay=0;
5438 default_frame_timeout=0;
5441 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5443 skip_to_iend=MagickFalse;
5444 term_chunk_found=MagickFalse;
5445 mng_info->framing_mode=1;
5446 #if defined(MNG_INSERT_LAYERS)
5447 mandatory_back=MagickFalse;
5449 #if defined(MNG_INSERT_LAYERS)
5450 mng_background_color=image->background_color;
5452 default_fb=mng_info->frame;
5453 previous_fb=mng_info->frame;
5457 type[MagickPathExtent];
5459 if (LocaleCompare(image_info->magick,"MNG") == 0)
5468 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5469 length=ReadBlobMSBLong(image);
5470 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5472 if (logging != MagickFalse)
5473 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5474 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5475 type[0],type[1],type[2],type[3],(double) length);
5477 if (length > PNG_UINT_31_MAX)
5484 ThrowReaderException(CorruptImageError,"CorruptImage");
5487 chunk=(unsigned char *) NULL;
5491 chunk=(unsigned char *) AcquireQuantumMemory(length,
5494 if (chunk == (unsigned char *) NULL)
5495 ThrowReaderException(ResourceLimitError,
5496 "MemoryAllocationFailed");
5498 for (i=0; i < (ssize_t) length; i++)
5499 chunk[i]=(unsigned char) ReadBlobByte(image);
5504 (void) ReadBlobMSBLong(image); /* read crc word */
5506 #if !defined(JNG_SUPPORTED)
5507 if (memcmp(type,mng_JHDR,4) == 0)
5509 skip_to_iend=MagickTrue;
5511 if (mng_info->jhdr_warning == 0)
5512 (void) ThrowMagickException(exception,GetMagickModule(),
5513 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5515 mng_info->jhdr_warning++;
5518 if (memcmp(type,mng_DHDR,4) == 0)
5520 skip_to_iend=MagickTrue;
5522 if (mng_info->dhdr_warning == 0)
5523 (void) ThrowMagickException(exception,GetMagickModule(),
5524 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5526 mng_info->dhdr_warning++;
5528 if (memcmp(type,mng_MEND,4) == 0)
5533 if (memcmp(type,mng_IEND,4) == 0)
5534 skip_to_iend=MagickFalse;
5537 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5539 if (logging != MagickFalse)
5540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5546 if (memcmp(type,mng_MHDR,4) == 0)
5551 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5552 ThrowReaderException(CorruptImageError,"CorruptImage");
5555 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5556 (p[2] << 8) | p[3]);
5558 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5559 (p[6] << 8) | p[7]);
5561 if (logging != MagickFalse)
5563 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5564 " MNG width: %.20g",(double) mng_info->mng_width);
5565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5566 " MNG height: %.20g",(double) mng_info->mng_height);
5570 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5572 if (mng_info->ticks_per_second == 0)
5573 default_frame_delay=0;
5576 default_frame_delay=1UL*image->ticks_per_second/
5577 mng_info->ticks_per_second;
5579 frame_delay=default_frame_delay;
5583 simplicity=(size_t) mng_get_long(p);
5585 mng_type=1; /* Full MNG */
5587 if ((simplicity != 0) && ((simplicity | 11) == 11))
5588 mng_type=2; /* LC */
5590 if ((simplicity != 0) && ((simplicity | 9) == 9))
5591 mng_type=3; /* VLC */
5593 #if defined(MNG_INSERT_LAYERS)
5595 insert_layers=MagickTrue;
5597 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5599 /* Allocate next image structure. */
5600 AcquireNextImage(image_info,image,exception);
5602 if (GetNextImageInList(image) == (Image *) NULL)
5603 return((Image *) NULL);
5605 image=SyncNextImageInList(image);
5606 mng_info->image=image;
5609 if ((mng_info->mng_width > 65535L) ||
5610 (mng_info->mng_height > 65535L))
5611 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5613 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5614 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5615 mng_info->mng_height);
5617 mng_info->frame.left=0;
5618 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5619 mng_info->frame.top=0;
5620 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5621 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5623 for (i=0; i < MNG_MAX_OBJECTS; i++)
5624 mng_info->object_clip[i]=mng_info->frame;
5626 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5630 if (memcmp(type,mng_TERM,4) == 0)
5640 final_delay=(png_uint_32) mng_get_long(&p[2]);
5641 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5643 if (mng_iterations == PNG_UINT_31_MAX)
5646 image->iterations=mng_iterations;
5647 term_chunk_found=MagickTrue;
5650 if (logging != MagickFalse)
5652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5653 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5654 repeat,(double) final_delay, (double) image->iterations);
5657 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5660 if (memcmp(type,mng_DEFI,4) == 0)
5663 (void) ThrowMagickException(exception,GetMagickModule(),
5664 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5671 ThrowReaderException(CorruptImageError,"CorruptImage");
5674 object_id=(p[0] << 8) | p[1];
5676 if (mng_type == 2 && object_id != 0)
5677 (void) ThrowMagickException(exception,GetMagickModule(),
5678 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5681 if (object_id > MNG_MAX_OBJECTS)
5684 Instead of using a warning we should allocate a larger
5685 MngInfo structure and continue.
5687 (void) ThrowMagickException(exception,GetMagickModule(),
5688 CoderError,"object id too large","`%s'",image->filename);
5689 object_id=MNG_MAX_OBJECTS;
5692 if (mng_info->exists[object_id])
5693 if (mng_info->frozen[object_id])
5695 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5696 (void) ThrowMagickException(exception,
5697 GetMagickModule(),CoderError,
5698 "DEFI cannot redefine a frozen MNG object","`%s'",
5703 mng_info->exists[object_id]=MagickTrue;
5706 mng_info->invisible[object_id]=p[2];
5709 Extract object offset info.
5713 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5714 (p[5] << 16) | (p[6] << 8) | p[7]);
5716 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5717 (p[9] << 16) | (p[10] << 8) | p[11]);
5719 if (logging != MagickFalse)
5721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5722 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5723 object_id,(double) mng_info->x_off[object_id],
5724 object_id,(double) mng_info->y_off[object_id]);
5729 Extract object clipping info.
5732 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5735 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5738 if (memcmp(type,mng_bKGD,4) == 0)
5740 mng_info->have_global_bkgd=MagickFalse;
5744 mng_info->mng_global_bkgd.red=
5745 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5747 mng_info->mng_global_bkgd.green=
5748 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5750 mng_info->mng_global_bkgd.blue=
5751 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5753 mng_info->have_global_bkgd=MagickTrue;
5756 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5759 if (memcmp(type,mng_BACK,4) == 0)
5761 #if defined(MNG_INSERT_LAYERS)
5763 mandatory_back=p[6];
5768 if (mandatory_back && length > 5)
5770 mng_background_color.red=
5771 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5773 mng_background_color.green=
5774 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5776 mng_background_color.blue=
5777 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5779 mng_background_color.alpha=OpaqueAlpha;
5782 #ifdef MNG_OBJECT_BUFFERS
5784 mng_background_object=(p[7] << 8) | p[8];
5787 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5791 if (memcmp(type,mng_PLTE,4) == 0)
5793 /* Read global PLTE. */
5795 if (length && (length < 769))
5797 if (mng_info->global_plte == (png_colorp) NULL)
5798 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5799 sizeof(*mng_info->global_plte));
5801 for (i=0; i < (ssize_t) (length/3); i++)
5803 mng_info->global_plte[i].red=p[3*i];
5804 mng_info->global_plte[i].green=p[3*i+1];
5805 mng_info->global_plte[i].blue=p[3*i+2];
5808 mng_info->global_plte_length=(unsigned int) (length/3);
5811 for ( ; i < 256; i++)
5813 mng_info->global_plte[i].red=i;
5814 mng_info->global_plte[i].green=i;
5815 mng_info->global_plte[i].blue=i;
5819 mng_info->global_plte_length=256;
5822 mng_info->global_plte_length=0;
5824 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5828 if (memcmp(type,mng_tRNS,4) == 0)
5830 /* read global tRNS */
5832 if (length > 0 && length < 257)
5833 for (i=0; i < (ssize_t) length; i++)
5834 mng_info->global_trns[i]=p[i];
5837 for ( ; i < 256; i++)
5838 mng_info->global_trns[i]=255;
5840 mng_info->global_trns_length=(unsigned int) length;
5841 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5844 if (memcmp(type,mng_gAMA,4) == 0)
5851 igamma=mng_get_long(p);
5852 mng_info->global_gamma=((float) igamma)*0.00001;
5853 mng_info->have_global_gama=MagickTrue;
5857 mng_info->have_global_gama=MagickFalse;
5859 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5863 if (memcmp(type,mng_cHRM,4) == 0)
5865 /* Read global cHRM */
5869 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5870 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5871 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5872 mng_info->global_chrm.red_primary.y=0.00001*
5873 mng_get_long(&p[12]);
5874 mng_info->global_chrm.green_primary.x=0.00001*
5875 mng_get_long(&p[16]);
5876 mng_info->global_chrm.green_primary.y=0.00001*
5877 mng_get_long(&p[20]);
5878 mng_info->global_chrm.blue_primary.x=0.00001*
5879 mng_get_long(&p[24]);
5880 mng_info->global_chrm.blue_primary.y=0.00001*
5881 mng_get_long(&p[28]);
5882 mng_info->have_global_chrm=MagickTrue;
5885 mng_info->have_global_chrm=MagickFalse;
5887 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5891 if (memcmp(type,mng_sRGB,4) == 0)
5898 mng_info->global_srgb_intent=
5899 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5900 mng_info->have_global_srgb=MagickTrue;
5903 mng_info->have_global_srgb=MagickFalse;
5905 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5909 if (memcmp(type,mng_iCCP,4) == 0)
5917 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5922 if (memcmp(type,mng_FRAM,4) == 0)
5925 (void) ThrowMagickException(exception,GetMagickModule(),
5926 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5929 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5930 image->delay=frame_delay;
5932 frame_delay=default_frame_delay;
5933 frame_timeout=default_frame_timeout;
5938 mng_info->framing_mode=p[0];
5940 if (logging != MagickFalse)
5941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5942 " Framing_mode=%d",mng_info->framing_mode);
5946 /* Note the delay and frame clipping boundaries. */
5948 p++; /* framing mode */
5950 while (*p && ((p-chunk) < (ssize_t) length))
5951 p++; /* frame name */
5953 p++; /* frame name terminator */
5955 if ((p-chunk) < (ssize_t) (length-4))
5962 change_delay=(*p++);
5963 change_timeout=(*p++);
5964 change_clipping=(*p++);
5965 p++; /* change_sync */
5969 frame_delay=1UL*image->ticks_per_second*
5972 if (mng_info->ticks_per_second != 0)
5973 frame_delay/=mng_info->ticks_per_second;
5976 frame_delay=PNG_UINT_31_MAX;
5978 if (change_delay == 2)
5979 default_frame_delay=frame_delay;
5983 if (logging != MagickFalse)
5984 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5985 " Framing_delay=%.20g",(double) frame_delay);
5990 frame_timeout=1UL*image->ticks_per_second*
5993 if (mng_info->ticks_per_second != 0)
5994 frame_timeout/=mng_info->ticks_per_second;
5997 frame_timeout=PNG_UINT_31_MAX;
5999 if (change_timeout == 2)
6000 default_frame_timeout=frame_timeout;
6004 if (logging != MagickFalse)
6005 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6006 " Framing_timeout=%.20g",(double) frame_timeout);
6009 if (change_clipping)
6011 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
6015 if (logging != MagickFalse)
6016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6017 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
6018 (double) fb.left,(double) fb.right,(double) fb.top,
6019 (double) fb.bottom);
6021 if (change_clipping == 2)
6027 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6029 subframe_width=(size_t) (mng_info->clip.right
6030 -mng_info->clip.left);
6032 subframe_height=(size_t) (mng_info->clip.bottom
6033 -mng_info->clip.top);
6035 Insert a background layer behind the frame if framing_mode is 4.
6037 #if defined(MNG_INSERT_LAYERS)
6038 if (logging != MagickFalse)
6039 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6040 " subframe_width=%.20g, subframe_height=%.20g",(double)
6041 subframe_width,(double) subframe_height);
6043 if (insert_layers && (mng_info->framing_mode == 4) &&
6044 (subframe_width) && (subframe_height))
6046 /* Allocate next image structure. */
6047 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6049 AcquireNextImage(image_info,image,exception);
6051 if (GetNextImageInList(image) == (Image *) NULL)
6053 image=DestroyImageList(image);
6054 MngInfoFreeStruct(mng_info,&have_mng_structure);
6055 return((Image *) NULL);
6058 image=SyncNextImageInList(image);
6061 mng_info->image=image;
6063 if (term_chunk_found)
6065 image->start_loop=MagickTrue;
6066 image->iterations=mng_iterations;
6067 term_chunk_found=MagickFalse;
6071 image->start_loop=MagickFalse;
6073 image->columns=subframe_width;
6074 image->rows=subframe_height;
6075 image->page.width=subframe_width;
6076 image->page.height=subframe_height;
6077 image->page.x=mng_info->clip.left;
6078 image->page.y=mng_info->clip.top;
6079 image->background_color=mng_background_color;
6080 image->alpha_trait=UndefinedPixelTrait;
6082 (void) SetImageBackgroundColor(image,exception);
6084 if (logging != MagickFalse)
6085 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6086 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6087 (double) mng_info->clip.left,
6088 (double) mng_info->clip.right,
6089 (double) mng_info->clip.top,
6090 (double) mng_info->clip.bottom);
6093 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6097 if (memcmp(type,mng_CLIP,4) == 0)
6108 first_object=(p[0] << 8) | p[1];
6109 last_object=(p[2] << 8) | p[3];
6112 for (i=(int) first_object; i <= (int) last_object; i++)
6114 if (mng_info->exists[i] && !mng_info->frozen[i])
6119 box=mng_info->object_clip[i];
6120 if ((p-chunk) < (ssize_t) (length-17))
6121 mng_info->object_clip[i]=
6122 mng_read_box(box,(char) p[0],&p[1]);
6127 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6131 if (memcmp(type,mng_SAVE,4) == 0)
6133 for (i=1; i < MNG_MAX_OBJECTS; i++)
6134 if (mng_info->exists[i])
6136 mng_info->frozen[i]=MagickTrue;
6137 #ifdef MNG_OBJECT_BUFFERS
6138 if (mng_info->ob[i] != (MngBuffer *) NULL)
6139 mng_info->ob[i]->frozen=MagickTrue;
6144 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6149 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6151 /* Read DISC or SEEK. */
6153 if ((length == 0) || !memcmp(type,mng_SEEK,4))
6155 for (i=1; i < MNG_MAX_OBJECTS; i++)
6156 MngInfoDiscardObject(mng_info,i);
6164 for (j=1; j < (ssize_t) length; j+=2)
6166 i=p[j-1] << 8 | p[j];
6167 MngInfoDiscardObject(mng_info,i);
6172 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6177 if (memcmp(type,mng_MOVE,4) == 0)
6187 first_object=(p[0] << 8) | p[1];
6188 last_object=(p[2] << 8) | p[3];
6191 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6193 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6194 (p-chunk) < (ssize_t) (length-8))
6202 old_pair.a=mng_info->x_off[i];
6203 old_pair.b=mng_info->y_off[i];
6204 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6205 mng_info->x_off[i]=new_pair.a;
6206 mng_info->y_off[i]=new_pair.b;
6211 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6215 if (memcmp(type,mng_LOOP,4) == 0)
6217 ssize_t loop_iters=1;
6220 loop_level=chunk[0];
6221 mng_info->loop_active[loop_level]=1; /* mark loop active */
6223 /* Record starting point. */
6224 loop_iters=mng_get_long(&chunk[1]);
6226 if (logging != MagickFalse)
6227 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6228 " LOOP level %.20g has %.20g iterations ",
6229 (double) loop_level, (double) loop_iters);
6231 if (loop_iters == 0)
6232 skipping_loop=loop_level;
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],
6284 ThrowReaderException(CorruptImageError,
6285 "ImproperImageHeader");
6296 mng_info->loop_active[loop_level]=0;
6298 for (i=0; i < loop_level; i++)
6299 if (mng_info->loop_active[i] == 1)
6300 last_level=(short) i;
6301 loop_level=last_level;
6307 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6311 if (memcmp(type,mng_CLON,4) == 0)
6313 if (mng_info->clon_warning == 0)
6314 (void) ThrowMagickException(exception,GetMagickModule(),
6315 CoderError,"CLON is not implemented yet","`%s'",
6318 mng_info->clon_warning++;
6321 if (memcmp(type,mng_MAGN,4) == 0)
6336 magn_first=(p[0] << 8) | p[1];
6342 magn_last=(p[2] << 8) | p[3];
6345 magn_last=magn_first;
6346 #ifndef MNG_OBJECT_BUFFERS
6347 if (magn_first || magn_last)
6348 if (mng_info->magn_warning == 0)
6350 (void) ThrowMagickException(exception,
6351 GetMagickModule(),CoderError,
6352 "MAGN is not implemented yet for nonzero objects",
6353 "`%s'",image->filename);
6355 mng_info->magn_warning++;
6365 magn_mx=(p[5] << 8) | p[6];
6374 magn_my=(p[7] << 8) | p[8];
6383 magn_ml=(p[9] << 8) | p[10];
6392 magn_mr=(p[11] << 8) | p[12];
6401 magn_mt=(p[13] << 8) | p[14];
6410 magn_mb=(p[15] << 8) | p[16];
6422 magn_methy=magn_methx;
6425 if (magn_methx > 5 || magn_methy > 5)
6426 if (mng_info->magn_warning == 0)
6428 (void) ThrowMagickException(exception,
6429 GetMagickModule(),CoderError,
6430 "Unknown MAGN method in MNG datastream","`%s'",
6433 mng_info->magn_warning++;
6435 #ifdef MNG_OBJECT_BUFFERS
6436 /* Magnify existing objects in the range magn_first to magn_last */
6438 if (magn_first == 0 || magn_last == 0)
6440 /* Save the magnification factors for object 0 */
6441 mng_info->magn_mb=magn_mb;
6442 mng_info->magn_ml=magn_ml;
6443 mng_info->magn_mr=magn_mr;
6444 mng_info->magn_mt=magn_mt;
6445 mng_info->magn_mx=magn_mx;
6446 mng_info->magn_my=magn_my;
6447 mng_info->magn_methx=magn_methx;
6448 mng_info->magn_methy=magn_methy;
6452 if (memcmp(type,mng_PAST,4) == 0)
6454 if (mng_info->past_warning == 0)
6455 (void) ThrowMagickException(exception,GetMagickModule(),
6456 CoderError,"PAST is not implemented yet","`%s'",
6459 mng_info->past_warning++;
6462 if (memcmp(type,mng_SHOW,4) == 0)
6464 if (mng_info->show_warning == 0)
6465 (void) ThrowMagickException(exception,GetMagickModule(),
6466 CoderError,"SHOW is not implemented yet","`%s'",
6469 mng_info->show_warning++;
6472 if (memcmp(type,mng_sBIT,4) == 0)
6475 mng_info->have_global_sbit=MagickFalse;
6479 mng_info->global_sbit.gray=p[0];
6480 mng_info->global_sbit.red=p[0];
6481 mng_info->global_sbit.green=p[1];
6482 mng_info->global_sbit.blue=p[2];
6483 mng_info->global_sbit.alpha=p[3];
6484 mng_info->have_global_sbit=MagickTrue;
6487 if (memcmp(type,mng_pHYs,4) == 0)
6491 mng_info->global_x_pixels_per_unit=
6492 (size_t) mng_get_long(p);
6493 mng_info->global_y_pixels_per_unit=
6494 (size_t) mng_get_long(&p[4]);
6495 mng_info->global_phys_unit_type=p[8];
6496 mng_info->have_global_phys=MagickTrue;
6500 mng_info->have_global_phys=MagickFalse;
6502 if (memcmp(type,mng_pHYg,4) == 0)
6504 if (mng_info->phyg_warning == 0)
6505 (void) ThrowMagickException(exception,GetMagickModule(),
6506 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6508 mng_info->phyg_warning++;
6510 if (memcmp(type,mng_BASI,4) == 0)
6512 skip_to_iend=MagickTrue;
6514 if (mng_info->basi_warning == 0)
6515 (void) ThrowMagickException(exception,GetMagickModule(),
6516 CoderError,"BASI is not implemented yet","`%s'",
6519 mng_info->basi_warning++;
6520 #ifdef MNG_BASI_SUPPORTED
6521 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6522 (p[2] << 8) | p[3]);
6523 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6524 (p[6] << 8) | p[7]);
6525 basi_color_type=p[8];
6526 basi_compression_method=p[9];
6527 basi_filter_type=p[10];
6528 basi_interlace_method=p[11];
6530 basi_red=(p[12] << 8) & p[13];
6536 basi_green=(p[14] << 8) & p[15];
6542 basi_blue=(p[16] << 8) & p[17];
6548 basi_alpha=(p[18] << 8) & p[19];
6552 if (basi_sample_depth == 16)
6559 basi_viewable=p[20];
6565 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6569 if (memcmp(type,mng_IHDR,4)
6570 #if defined(JNG_SUPPORTED)
6571 && memcmp(type,mng_JHDR,4)
6575 /* Not an IHDR or JHDR chunk */
6577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6582 if (logging != MagickFalse)
6583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6584 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6586 mng_info->exists[object_id]=MagickTrue;
6587 mng_info->viewable[object_id]=MagickTrue;
6589 if (mng_info->invisible[object_id])
6591 if (logging != MagickFalse)
6592 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6593 " Skipping invisible object");
6595 skip_to_iend=MagickTrue;
6596 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6599 #if defined(MNG_INSERT_LAYERS)
6601 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6603 image_width=(size_t) mng_get_long(p);
6604 image_height=(size_t) mng_get_long(&p[4]);
6606 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6609 Insert a transparent background layer behind the entire animation
6610 if it is not full screen.
6612 #if defined(MNG_INSERT_LAYERS)
6613 if (insert_layers && mng_type && first_mng_object)
6615 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6616 (image_width < mng_info->mng_width) ||
6617 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6618 (image_height < mng_info->mng_height) ||
6619 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6621 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6624 Allocate next image structure.
6626 AcquireNextImage(image_info,image,exception);
6628 if (GetNextImageInList(image) == (Image *) NULL)
6630 image=DestroyImageList(image);
6631 MngInfoFreeStruct(mng_info,&have_mng_structure);
6632 return((Image *) NULL);
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)
6683 image=DestroyImageList(image);
6684 MngInfoFreeStruct(mng_info,&have_mng_structure);
6685 return((Image *) NULL);
6688 image=SyncNextImageInList(image);
6691 mng_info->image=image;
6693 if (term_chunk_found)
6695 image->start_loop=MagickTrue;
6696 image->iterations=mng_iterations;
6697 term_chunk_found=MagickFalse;
6701 image->start_loop=MagickFalse;
6704 image->columns=subframe_width;
6705 image->rows=subframe_height;
6706 image->page.width=subframe_width;
6707 image->page.height=subframe_height;
6708 image->page.x=mng_info->clip.left;
6709 image->page.y=mng_info->clip.top;
6710 image->background_color=mng_background_color;
6711 image->alpha_trait=UndefinedPixelTrait;
6712 (void) SetImageBackgroundColor(image,exception);
6714 if (logging != MagickFalse)
6715 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6716 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6717 (double) mng_info->clip.left,(double) mng_info->clip.right,
6718 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6720 #endif /* MNG_INSERT_LAYERS */
6721 first_mng_object=MagickFalse;
6723 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6726 Allocate next image structure.
6728 AcquireNextImage(image_info,image,exception);
6730 if (GetNextImageInList(image) == (Image *) NULL)
6732 image=DestroyImageList(image);
6733 MngInfoFreeStruct(mng_info,&have_mng_structure);
6734 return((Image *) NULL);
6737 image=SyncNextImageInList(image);
6739 mng_info->image=image;
6740 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6741 GetBlobSize(image));
6743 if (status == MagickFalse)
6746 if (term_chunk_found)
6748 image->start_loop=MagickTrue;
6749 term_chunk_found=MagickFalse;
6753 image->start_loop=MagickFalse;
6755 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6757 image->delay=frame_delay;
6758 frame_delay=default_frame_delay;
6764 image->page.width=mng_info->mng_width;
6765 image->page.height=mng_info->mng_height;
6766 image->page.x=mng_info->x_off[object_id];
6767 image->page.y=mng_info->y_off[object_id];
6768 image->iterations=mng_iterations;
6771 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6774 if (logging != MagickFalse)
6775 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6776 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6779 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6782 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6785 mng_info->image=image;
6786 mng_info->mng_type=mng_type;
6787 mng_info->object_id=object_id;
6789 if (memcmp(type,mng_IHDR,4) == 0)
6790 image=ReadOnePNGImage(mng_info,image_info,exception);
6792 #if defined(JNG_SUPPORTED)
6794 image=ReadOneJNGImage(mng_info,image_info,exception);
6797 if (image == (Image *) NULL)
6799 if (logging != MagickFalse)
6800 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6801 "exit ReadJNGImage() with error");
6803 MngInfoFreeStruct(mng_info,&have_mng_structure);
6804 return((Image *) NULL);
6807 if (image->columns == 0 || image->rows == 0)
6809 (void) CloseBlob(image);
6810 image=DestroyImageList(image);
6811 MngInfoFreeStruct(mng_info,&have_mng_structure);
6812 return((Image *) NULL);
6815 mng_info->image=image;
6822 if (mng_info->magn_methx || mng_info->magn_methy)
6828 if (logging != MagickFalse)
6829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6830 " Processing MNG MAGN chunk");
6832 if (mng_info->magn_methx == 1)
6834 magnified_width=mng_info->magn_ml;
6836 if (image->columns > 1)
6837 magnified_width += mng_info->magn_mr;
6839 if (image->columns > 2)
6840 magnified_width += (png_uint_32)
6841 ((image->columns-2)*(mng_info->magn_mx));
6846 magnified_width=(png_uint_32) image->columns;
6848 if (image->columns > 1)
6849 magnified_width += mng_info->magn_ml-1;
6851 if (image->columns > 2)
6852 magnified_width += mng_info->magn_mr-1;
6854 if (image->columns > 3)
6855 magnified_width += (png_uint_32)
6856 ((image->columns-3)*(mng_info->magn_mx-1));
6859 if (mng_info->magn_methy == 1)
6861 magnified_height=mng_info->magn_mt;
6863 if (image->rows > 1)
6864 magnified_height += mng_info->magn_mb;
6866 if (image->rows > 2)
6867 magnified_height += (png_uint_32)
6868 ((image->rows-2)*(mng_info->magn_my));
6873 magnified_height=(png_uint_32) image->rows;
6875 if (image->rows > 1)
6876 magnified_height += mng_info->magn_mt-1;
6878 if (image->rows > 2)
6879 magnified_height += mng_info->magn_mb-1;
6881 if (image->rows > 3)
6882 magnified_height += (png_uint_32)
6883 ((image->rows-3)*(mng_info->magn_my-1));
6886 if (magnified_height > image->rows ||
6887 magnified_width > image->columns)
6914 /* Allocate next image structure. */
6916 if (logging != MagickFalse)
6917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6918 " Allocate magnified image");
6920 AcquireNextImage(image_info,image,exception);
6922 if (GetNextImageInList(image) == (Image *) NULL)
6924 image=DestroyImageList(image);
6925 MngInfoFreeStruct(mng_info,&have_mng_structure);
6926 return((Image *) NULL);
6929 large_image=SyncNextImageInList(image);
6931 large_image->columns=magnified_width;
6932 large_image->rows=magnified_height;
6934 magn_methx=mng_info->magn_methx;
6935 magn_methy=mng_info->magn_methy;
6937 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6938 #define QM unsigned short
6939 if (magn_methx != 1 || magn_methy != 1)
6942 Scale pixels to unsigned shorts to prevent
6943 overflow of intermediate values of interpolations
6945 for (y=0; y < (ssize_t) image->rows; y++)
6947 q=GetAuthenticPixels(image,0,y,image->columns,1,
6950 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6952 SetPixelRed(image,ScaleQuantumToShort(
6953 GetPixelRed(image,q)),q);
6954 SetPixelGreen(image,ScaleQuantumToShort(
6955 GetPixelGreen(image,q)),q);
6956 SetPixelBlue(image,ScaleQuantumToShort(
6957 GetPixelBlue(image,q)),q);
6958 SetPixelAlpha(image,ScaleQuantumToShort(
6959 GetPixelAlpha(image,q)),q);
6960 q+=GetPixelChannels(image);
6963 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6971 if (image->alpha_trait != UndefinedPixelTrait)
6972 (void) SetImageBackgroundColor(large_image,exception);
6976 large_image->background_color.alpha=OpaqueAlpha;
6977 (void) SetImageBackgroundColor(large_image,exception);
6979 if (magn_methx == 4)
6982 if (magn_methx == 5)
6985 if (magn_methy == 4)
6988 if (magn_methy == 5)
6992 /* magnify the rows into the right side of the large image */
6994 if (logging != MagickFalse)
6995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6996 " Magnify the rows to %.20g",
6997 (double) large_image->rows);
6998 m=(ssize_t) mng_info->magn_mt;
7000 length=(size_t) GetPixelChannels(image)*image->columns;
7001 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
7002 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
7004 if ((prev == (Quantum *) NULL) ||
7005 (next == (Quantum *) NULL))
7007 image=DestroyImageList(image);
7008 MngInfoFreeStruct(mng_info,&have_mng_structure);
7009 ThrowReaderException(ResourceLimitError,
7010 "MemoryAllocationFailed");
7013 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
7014 (void) CopyMagickMemory(next,n,length);
7016 for (y=0; y < (ssize_t) image->rows; y++)
7019 m=(ssize_t) mng_info->magn_mt;
7021 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
7022 m=(ssize_t) mng_info->magn_mb;
7024 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
7025 m=(ssize_t) mng_info->magn_mb;
7027 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
7031 m=(ssize_t) mng_info->magn_my;
7037 if (y < (ssize_t) image->rows-1)
7039 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
7041 (void) CopyMagickMemory(next,n,length);
7044 for (i=0; i < m; i++, yy++)
7049 assert(yy < (ssize_t) large_image->rows);
7052 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7054 q+=(large_image->columns-image->columns)*
7055 GetPixelChannels(large_image);
7057 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7059 /* To do: get color as function of indexes[x] */
7061 if (image->storage_class == PseudoClass)
7066 if (magn_methy <= 1)
7068 /* replicate previous */
7069 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7070 SetPixelGreen(large_image,GetPixelGreen(image,
7072 SetPixelBlue(large_image,GetPixelBlue(image,
7074 SetPixelAlpha(large_image,GetPixelAlpha(image,
7078 else if (magn_methy == 2 || magn_methy == 4)
7082 SetPixelRed(large_image,GetPixelRed(image,
7084 SetPixelGreen(large_image,GetPixelGreen(image,
7086 SetPixelBlue(large_image,GetPixelBlue(image,
7088 SetPixelAlpha(large_image,GetPixelAlpha(image,
7095 SetPixelRed(large_image,((QM) (((ssize_t)
7096 (2*i*(GetPixelRed(image,n)
7097 -GetPixelRed(image,pixels)+m))/
7099 +GetPixelRed(image,pixels)))),q);
7100 SetPixelGreen(large_image,((QM) (((ssize_t)
7101 (2*i*(GetPixelGreen(image,n)
7102 -GetPixelGreen(image,pixels)+m))/
7104 +GetPixelGreen(image,pixels)))),q);
7105 SetPixelBlue(large_image,((QM) (((ssize_t)
7106 (2*i*(GetPixelBlue(image,n)
7107 -GetPixelBlue(image,pixels)+m))/
7109 +GetPixelBlue(image,pixels)))),q);
7111 if (image->alpha_trait != UndefinedPixelTrait)
7112 SetPixelAlpha(large_image, ((QM) (((ssize_t)
7113 (2*i*(GetPixelAlpha(image,n)
7114 -GetPixelAlpha(image,pixels)+m))
7116 GetPixelAlpha(image,pixels)))),q);
7119 if (magn_methy == 4)
7121 /* Replicate nearest */
7122 if (i <= ((m+1) << 1))
7123 SetPixelAlpha(large_image,GetPixelAlpha(image,
7126 SetPixelAlpha(large_image,GetPixelAlpha(image,
7131 else /* if (magn_methy == 3 || magn_methy == 5) */
7133 /* Replicate nearest */
7134 if (i <= ((m+1) << 1))
7136 SetPixelRed(large_image,GetPixelRed(image,
7138 SetPixelGreen(large_image,GetPixelGreen(image,
7140 SetPixelBlue(large_image,GetPixelBlue(image,
7142 SetPixelAlpha(large_image,GetPixelAlpha(image,
7148 SetPixelRed(large_image,GetPixelRed(image,n),q);
7149 SetPixelGreen(large_image,GetPixelGreen(image,n),
7151 SetPixelBlue(large_image,GetPixelBlue(image,n),
7153 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7157 if (magn_methy == 5)
7159 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7160 (GetPixelAlpha(image,n)
7161 -GetPixelAlpha(image,pixels))
7162 +m))/((ssize_t) (m*2))
7163 +GetPixelAlpha(image,pixels)),q);
7166 n+=GetPixelChannels(image);
7167 q+=GetPixelChannels(large_image);
7168 pixels+=GetPixelChannels(image);
7171 if (SyncAuthenticPixels(large_image,exception) == 0)
7177 prev=(Quantum *) RelinquishMagickMemory(prev);
7178 next=(Quantum *) RelinquishMagickMemory(next);
7180 length=image->columns;
7182 if (logging != MagickFalse)
7183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7184 " Delete original image");
7186 DeleteImageFromList(&image);
7190 mng_info->image=image;
7192 /* magnify the columns */
7193 if (logging != MagickFalse)
7194 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7195 " Magnify the columns to %.20g",
7196 (double) image->columns);
7198 for (y=0; y < (ssize_t) image->rows; y++)
7203 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7204 pixels=q+(image->columns-length)*GetPixelChannels(image);
7205 n=pixels+GetPixelChannels(image);
7207 for (x=(ssize_t) (image->columns-length);
7208 x < (ssize_t) image->columns; x++)
7210 /* To do: Rewrite using Get/Set***PixelChannel() */
7212 if (x == (ssize_t) (image->columns-length))
7213 m=(ssize_t) mng_info->magn_ml;
7215 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7216 m=(ssize_t) mng_info->magn_mr;
7218 else if (magn_methx <= 1 &&
7219 x == (ssize_t) image->columns-1)
7220 m=(ssize_t) mng_info->magn_mr;
7222 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7226 m=(ssize_t) mng_info->magn_mx;
7228 for (i=0; i < m; i++)
7230 if (magn_methx <= 1)
7232 /* replicate previous */
7233 SetPixelRed(image,GetPixelRed(image,pixels),q);
7234 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7235 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7236 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7239 else if (magn_methx == 2 || magn_methx == 4)
7243 SetPixelRed(image,GetPixelRed(image,pixels),q);
7244 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7245 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7246 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7249 /* To do: Rewrite using Get/Set***PixelChannel() */
7253 SetPixelRed(image,(QM) ((2*i*(
7254 GetPixelRed(image,n)
7255 -GetPixelRed(image,pixels))+m)
7257 GetPixelRed(image,pixels)),q);
7259 SetPixelGreen(image,(QM) ((2*i*(
7260 GetPixelGreen(image,n)
7261 -GetPixelGreen(image,pixels))+m)
7263 GetPixelGreen(image,pixels)),q);
7265 SetPixelBlue(image,(QM) ((2*i*(
7266 GetPixelBlue(image,n)
7267 -GetPixelBlue(image,pixels))+m)
7269 GetPixelBlue(image,pixels)),q);
7270 if (image->alpha_trait != UndefinedPixelTrait)
7271 SetPixelAlpha(image,(QM) ((2*i*(
7272 GetPixelAlpha(image,n)
7273 -GetPixelAlpha(image,pixels))+m)
7275 GetPixelAlpha(image,pixels)),q);
7278 if (magn_methx == 4)
7280 /* Replicate nearest */
7281 if (i <= ((m+1) << 1))
7283 SetPixelAlpha(image,
7284 GetPixelAlpha(image,pixels)+0,q);
7288 SetPixelAlpha(image,
7289 GetPixelAlpha(image,n)+0,q);
7294 else /* if (magn_methx == 3 || magn_methx == 5) */
7296 /* Replicate nearest */
7297 if (i <= ((m+1) << 1))
7299 SetPixelRed(image,GetPixelRed(image,pixels),q);
7300 SetPixelGreen(image,GetPixelGreen(image,
7302 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7303 SetPixelAlpha(image,GetPixelAlpha(image,
7309 SetPixelRed(image,GetPixelRed(image,n),q);
7310 SetPixelGreen(image,GetPixelGreen(image,n),q);
7311 SetPixelBlue(image,GetPixelBlue(image,n),q);
7312 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7315 if (magn_methx == 5)
7318 SetPixelAlpha(image,
7319 (QM) ((2*i*( GetPixelAlpha(image,n)
7320 -GetPixelAlpha(image,pixels))+m)/
7322 +GetPixelAlpha(image,pixels)),q);
7325 q+=GetPixelChannels(image);
7327 n+=GetPixelChannels(image);
7330 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7333 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7334 if (magn_methx != 1 || magn_methy != 1)
7337 Rescale pixels to Quantum
7339 for (y=0; y < (ssize_t) image->rows; y++)
7341 q=GetAuthenticPixels(image,0,y,image->columns,1,
7344 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7346 SetPixelRed(image,ScaleShortToQuantum(
7347 GetPixelRed(image,q)),q);
7348 SetPixelGreen(image,ScaleShortToQuantum(
7349 GetPixelGreen(image,q)),q);
7350 SetPixelBlue(image,ScaleShortToQuantum(
7351 GetPixelBlue(image,q)),q);
7352 SetPixelAlpha(image,ScaleShortToQuantum(
7353 GetPixelAlpha(image,q)),q);
7354 q+=GetPixelChannels(image);
7357 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7362 if (logging != MagickFalse)
7363 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7364 " Finished MAGN processing");
7369 Crop_box is with respect to the upper left corner of the MNG.
7371 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7372 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7373 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7374 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7375 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7376 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7377 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7378 if ((crop_box.left != (mng_info->image_box.left
7379 +mng_info->x_off[object_id])) ||
7380 (crop_box.right != (mng_info->image_box.right
7381 +mng_info->x_off[object_id])) ||
7382 (crop_box.top != (mng_info->image_box.top
7383 +mng_info->y_off[object_id])) ||
7384 (crop_box.bottom != (mng_info->image_box.bottom
7385 +mng_info->y_off[object_id])))
7387 if (logging != MagickFalse)
7388 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7389 " Crop the PNG image");
7391 if ((crop_box.left < crop_box.right) &&
7392 (crop_box.top < crop_box.bottom))
7401 Crop_info is with respect to the upper left corner of
7404 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7405 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7406 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7407 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7408 image->page.width=image->columns;
7409 image->page.height=image->rows;
7412 im=CropImage(image,&crop_info,exception);
7414 if (im != (Image *) NULL)
7416 image->columns=im->columns;
7417 image->rows=im->rows;
7418 im=DestroyImage(im);
7419 image->page.width=image->columns;
7420 image->page.height=image->rows;
7421 image->page.x=crop_box.left;
7422 image->page.y=crop_box.top;
7429 No pixels in crop area. The MNG spec still requires
7430 a layer, though, so make a single transparent pixel in
7431 the top left corner.
7436 (void) SetImageBackgroundColor(image,exception);
7437 image->page.width=1;
7438 image->page.height=1;
7443 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7444 image=mng_info->image;
7448 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7449 /* PNG does not handle depths greater than 16 so reduce it even
7452 if (image->depth > 16)
7456 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7457 if (image->depth > 8)
7459 /* To do: fill low byte properly */
7463 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7467 if (image_info->number_scenes != 0)
7469 if (mng_info->scenes_found >
7470 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7474 if (logging != MagickFalse)
7475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7476 " Finished reading image datastream.");
7478 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7480 (void) CloseBlob(image);
7482 if (logging != MagickFalse)
7483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7484 " Finished reading all image datastreams.");
7486 #if defined(MNG_INSERT_LAYERS)
7487 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7488 (mng_info->mng_height))
7491 Insert a background layer if nothing else was found.
7493 if (logging != MagickFalse)
7494 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7495 " No images found. Inserting a background layer.");
7497 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7500 Allocate next image structure.
7502 AcquireNextImage(image_info,image,exception);
7503 if (GetNextImageInList(image) == (Image *) NULL)
7505 image=DestroyImageList(image);
7506 MngInfoFreeStruct(mng_info,&have_mng_structure);
7508 if (logging != MagickFalse)
7509 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7510 " Allocation failed, returning NULL.");
7512 return((Image *) NULL);
7514 image=SyncNextImageInList(image);
7516 image->columns=mng_info->mng_width;
7517 image->rows=mng_info->mng_height;
7518 image->page.width=mng_info->mng_width;
7519 image->page.height=mng_info->mng_height;
7522 image->background_color=mng_background_color;
7523 image->alpha_trait=UndefinedPixelTrait;
7525 if (image_info->ping == MagickFalse)
7526 (void) SetImageBackgroundColor(image,exception);
7528 mng_info->image_found++;
7531 image->iterations=mng_iterations;
7533 if (mng_iterations == 1)
7534 image->start_loop=MagickTrue;
7536 while (GetPreviousImageInList(image) != (Image *) NULL)
7539 if (image_count > 10*mng_info->image_found)
7541 if (logging != MagickFalse)
7542 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7544 (void) ThrowMagickException(exception,GetMagickModule(),
7545 CoderError,"Linked list is corrupted, beginning of list not found",
7546 "`%s'",image_info->filename);
7548 return((Image *) NULL);
7551 image=GetPreviousImageInList(image);
7553 if (GetNextImageInList(image) == (Image *) NULL)
7555 if (logging != MagickFalse)
7556 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7558 (void) ThrowMagickException(exception,GetMagickModule(),
7559 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7560 image_info->filename);
7564 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7565 GetNextImageInList(image) ==
7568 if (logging != MagickFalse)
7569 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7570 " First image null");
7572 (void) ThrowMagickException(exception,GetMagickModule(),
7573 CoderError,"image->next for first image is NULL but shouldn't be.",
7574 "`%s'",image_info->filename);
7577 if (mng_info->image_found == 0)
7579 if (logging != MagickFalse)
7580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7581 " No visible images found.");
7583 (void) ThrowMagickException(exception,GetMagickModule(),
7584 CoderError,"No visible images in file","`%s'",image_info->filename);
7586 if (image != (Image *) NULL)
7587 image=DestroyImageList(image);
7589 MngInfoFreeStruct(mng_info,&have_mng_structure);
7590 return((Image *) NULL);
7593 if (mng_info->ticks_per_second)
7594 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7595 final_delay/mng_info->ticks_per_second;
7598 image->start_loop=MagickTrue;
7600 /* Find final nonzero image delay */
7601 final_image_delay=0;
7603 while (GetNextImageInList(image) != (Image *) NULL)
7606 final_image_delay=image->delay;
7608 image=GetNextImageInList(image);
7611 if (final_delay < final_image_delay)
7612 final_delay=final_image_delay;
7614 image->delay=final_delay;
7616 if (logging != MagickFalse)
7617 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7618 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7619 (double) final_delay);
7621 if (logging != MagickFalse)
7627 image=GetFirstImageInList(image);
7629 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7630 " Before coalesce:");
7632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7633 " scene 0 delay=%.20g",(double) image->delay);
7635 while (GetNextImageInList(image) != (Image *) NULL)
7637 image=GetNextImageInList(image);
7638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7639 " scene %.20g delay=%.20g",(double) scene++,
7640 (double) image->delay);
7644 image=GetFirstImageInList(image);
7645 #ifdef MNG_COALESCE_LAYERS
7655 if (logging != MagickFalse)
7656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7657 " Coalesce Images");
7660 next_image=CoalesceImages(image,exception);
7662 if (next_image == (Image *) NULL)
7663 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7665 image=DestroyImageList(image);
7668 for (next=image; next != (Image *) NULL; next=next_image)
7670 next->page.width=mng_info->mng_width;
7671 next->page.height=mng_info->mng_height;
7674 next->scene=scene++;
7675 next_image=GetNextImageInList(next);
7677 if (next_image == (Image *) NULL)
7680 if (next->delay == 0)
7683 next_image->previous=GetPreviousImageInList(next);
7684 if (GetPreviousImageInList(next) == (Image *) NULL)
7687 next->previous->next=next_image;
7688 next=DestroyImage(next);
7694 while (GetNextImageInList(image) != (Image *) NULL)
7695 image=GetNextImageInList(image);
7697 image->dispose=BackgroundDispose;
7699 if (logging != MagickFalse)
7705 image=GetFirstImageInList(image);
7707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7708 " After coalesce:");
7710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7711 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7712 (double) image->dispose);
7714 while (GetNextImageInList(image) != (Image *) NULL)
7716 image=GetNextImageInList(image);
7718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7719 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7720 (double) image->delay,(double) image->dispose);
7724 image=GetFirstImageInList(image);
7725 MngInfoFreeStruct(mng_info,&have_mng_structure);
7726 have_mng_structure=MagickFalse;
7728 if (logging != MagickFalse)
7729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7731 return(GetFirstImageInList(image));
7733 #else /* PNG_LIBPNG_VER > 10011 */
7734 static Image *ReadPNGImage(const ImageInfo *image_info,
7735 ExceptionInfo *exception)
7737 printf("Your PNG library is too old: You have libpng-%s\n",
7738 PNG_LIBPNG_VER_STRING);
7740 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7741 "PNG library is too old","`%s'",image_info->filename);
7743 return(Image *) NULL;
7746 static Image *ReadMNGImage(const ImageInfo *image_info,
7747 ExceptionInfo *exception)
7749 return(ReadPNGImage(image_info,exception));
7751 #endif /* PNG_LIBPNG_VER > 10011 */
7755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7759 % R e g i s t e r P N G I m a g e %
7763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7765 % RegisterPNGImage() adds properties for the PNG image format to
7766 % the list of supported formats. The properties include the image format
7767 % tag, a method to read and/or write the format, whether the format
7768 % supports the saving of more than one frame to the same file or blob,
7769 % whether the format supports native in-memory I/O, and a brief
7770 % description of the format.
7772 % The format of the RegisterPNGImage method is:
7774 % size_t RegisterPNGImage(void)
7777 ModuleExport size_t RegisterPNGImage(void)
7780 version[MagickPathExtent];
7788 "See http://www.libpng.org/ for details about the PNG format."
7793 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7799 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7805 #if defined(PNG_LIBPNG_VER_STRING)
7806 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7807 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7810 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7812 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7813 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7818 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7819 entry->flags|=CoderSeekableStreamFlag; /* To do: eliminate this. */
7821 #if defined(MAGICKCORE_PNG_DELEGATE)
7822 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7823 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7826 entry->magick=(IsImageFormatHandler *) IsMNG;
7828 if (*version != '\0')
7829 entry->version=ConstantString(version);
7831 entry->mime_type=ConstantString("video/x-mng");
7832 entry->note=ConstantString(MNGNote);
7833 (void) RegisterMagickInfo(entry);
7835 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7837 #if defined(MAGICKCORE_PNG_DELEGATE)
7838 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7839 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7842 entry->magick=(IsImageFormatHandler *) IsPNG;
7843 entry->flags^=CoderAdjoinFlag;
7844 entry->mime_type=ConstantString("image/png");
7846 if (*version != '\0')
7847 entry->version=ConstantString(version);
7849 entry->note=ConstantString(PNGNote);
7850 (void) RegisterMagickInfo(entry);
7852 entry=AcquireMagickInfo("PNG","PNG8",
7853 "8-bit indexed with optional binary transparency");
7855 #if defined(MAGICKCORE_PNG_DELEGATE)
7856 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7857 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7860 entry->magick=(IsImageFormatHandler *) IsPNG;
7861 entry->flags^=CoderAdjoinFlag;
7862 entry->mime_type=ConstantString("image/png");
7863 (void) RegisterMagickInfo(entry);
7865 entry=AcquireMagickInfo("PNG","PNG24",
7866 "opaque or binary transparent 24-bit RGB");
7869 #if defined(ZLIB_VERSION)
7870 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7871 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7873 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7875 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7876 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7880 if (*version != '\0')
7881 entry->version=ConstantString(version);
7883 #if defined(MAGICKCORE_PNG_DELEGATE)
7884 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7885 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7888 entry->magick=(IsImageFormatHandler *) IsPNG;
7889 entry->flags^=CoderAdjoinFlag;
7890 entry->mime_type=ConstantString("image/png");
7891 (void) RegisterMagickInfo(entry);
7893 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7895 #if defined(MAGICKCORE_PNG_DELEGATE)
7896 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7897 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7900 entry->magick=(IsImageFormatHandler *) IsPNG;
7901 entry->flags^=CoderAdjoinFlag;
7902 entry->mime_type=ConstantString("image/png");
7903 (void) RegisterMagickInfo(entry);
7905 entry=AcquireMagickInfo("PNG","PNG48",
7906 "opaque or binary transparent 48-bit RGB");
7908 #if defined(MAGICKCORE_PNG_DELEGATE)
7909 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7910 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7913 entry->magick=(IsImageFormatHandler *) IsPNG;
7914 entry->flags^=CoderAdjoinFlag;
7915 entry->mime_type=ConstantString("image/png");
7916 (void) RegisterMagickInfo(entry);
7918 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7920 #if defined(MAGICKCORE_PNG_DELEGATE)
7921 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7922 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7925 entry->magick=(IsImageFormatHandler *) IsPNG;
7926 entry->flags^=CoderAdjoinFlag;
7927 entry->mime_type=ConstantString("image/png");
7928 (void) RegisterMagickInfo(entry);
7930 entry=AcquireMagickInfo("PNG","PNG00",
7931 "PNG inheriting bit-depth, color-type from original, if possible");
7933 #if defined(MAGICKCORE_PNG_DELEGATE)
7934 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7935 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7938 entry->magick=(IsImageFormatHandler *) IsPNG;
7939 entry->flags^=CoderAdjoinFlag;
7940 entry->mime_type=ConstantString("image/png");
7941 (void) RegisterMagickInfo(entry);
7943 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7945 #if defined(JNG_SUPPORTED)
7946 #if defined(MAGICKCORE_PNG_DELEGATE)
7947 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7948 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7952 entry->magick=(IsImageFormatHandler *) IsJNG;
7953 entry->flags^=CoderAdjoinFlag;
7954 entry->mime_type=ConstantString("image/x-jng");
7955 entry->note=ConstantString(JNGNote);
7956 (void) RegisterMagickInfo(entry);
7958 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7959 ping_semaphore=AcquireSemaphoreInfo();
7962 return(MagickImageCoderSignature);
7966 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7970 % U n r e g i s t e r P N G I m a g e %
7974 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7976 % UnregisterPNGImage() removes format registrations made by the
7977 % PNG module from the list of supported formats.
7979 % The format of the UnregisterPNGImage method is:
7981 % UnregisterPNGImage(void)
7984 ModuleExport void UnregisterPNGImage(void)
7986 (void) UnregisterMagickInfo("MNG");
7987 (void) UnregisterMagickInfo("PNG");
7988 (void) UnregisterMagickInfo("PNG8");
7989 (void) UnregisterMagickInfo("PNG24");
7990 (void) UnregisterMagickInfo("PNG32");
7991 (void) UnregisterMagickInfo("PNG48");
7992 (void) UnregisterMagickInfo("PNG64");
7993 (void) UnregisterMagickInfo("PNG00");
7994 (void) UnregisterMagickInfo("JNG");
7996 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7997 if (ping_semaphore != (SemaphoreInfo *) NULL)
7998 RelinquishSemaphoreInfo(&ping_semaphore);
8002 #if defined(MAGICKCORE_PNG_DELEGATE)
8003 #if PNG_LIBPNG_VER > 10011
8005 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8009 % W r i t e M N G I m a g e %
8013 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8015 % WriteMNGImage() writes an image in the Portable Network Graphics
8016 % Group's "Multiple-image Network Graphics" encoded image format.
8018 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
8020 % The format of the WriteMNGImage method is:
8022 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8023 % Image *image,ExceptionInfo *exception)
8025 % A description of each parameter follows.
8027 % o image_info: the image info.
8029 % o image: The image.
8031 % o exception: return any errors or warnings in this structure.
8033 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8034 % "To do" under ReadPNGImage):
8036 % Preserve all unknown and not-yet-handled known chunks found in input
8037 % PNG file and copy them into output PNG files according to the PNG
8040 % Write the iCCP chunk at MNG level when (icc profile length > 0)
8042 % Improve selection of color type (use indexed-colour or indexed-colour
8043 % with tRNS when 256 or fewer unique RGBA values are present).
8045 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8046 % This will be complicated if we limit ourselves to generating MNG-LC
8047 % files. For now we ignore disposal method 3 and simply overlay the next
8050 % Check for identical PLTE's or PLTE/tRNS combinations and use a
8051 % global MNG PLTE or PLTE/tRNS combination when appropriate.
8052 % [mostly done 15 June 1999 but still need to take care of tRNS]
8054 % Check for identical sRGB and replace with a global sRGB (and remove
8055 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8056 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8057 % local gAMA/cHRM with local sRGB if appropriate).
8059 % Check for identical sBIT chunks and write global ones.
8061 % Provide option to skip writing the signature tEXt chunks.
8063 % Use signatures to detect identical objects and reuse the first
8064 % instance of such objects instead of writing duplicate objects.
8066 % Use a smaller-than-32k value of compression window size when
8069 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
8070 % ancillary text chunks and save profiles.
8072 % Provide an option to force LC files (to ensure exact framing rate)
8075 % Provide an option to force VLC files instead of LC, even when offsets
8076 % are present. This will involve expanding the embedded images with a
8077 % transparent region at the top and/or left.
8081 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8082 png_info *ping_info, unsigned char *profile_type, unsigned char
8083 *profile_description, unsigned char *profile_data, png_uint_32 length)
8102 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8104 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8107 if (image_info->verbose)
8109 (void) printf("writing raw profile: type=%s, length=%.20g\n",
8110 (char *) profile_type, (double) length);
8113 #if PNG_LIBPNG_VER >= 10400
8114 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8116 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8118 description_length=(png_uint_32) strlen((const char *) profile_description);
8119 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8120 + description_length);
8121 #if PNG_LIBPNG_VER >= 10400
8122 text[0].text=(png_charp) png_malloc(ping,
8123 (png_alloc_size_t) allocated_length);
8124 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8126 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8127 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8129 text[0].key[0]='\0';
8130 (void) ConcatenateMagickString(text[0].key,
8131 "Raw profile type ",MagickPathExtent);
8132 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8136 (void) CopyMagickString(dp,(const char *) profile_description,
8138 dp+=description_length;
8140 (void) FormatLocaleString(dp,allocated_length-
8141 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8144 for (i=0; i < (ssize_t) length; i++)
8148 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8149 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8154 text[0].text_length=(png_size_t) (dp-text[0].text);
8155 text[0].compression=image_info->compression == NoCompression ||
8156 (image_info->compression == UndefinedCompression &&
8157 text[0].text_length < 128) ? -1 : 0;
8159 if (text[0].text_length <= allocated_length)
8160 png_set_text(ping,ping_info,text,1);
8162 png_free(ping,text[0].text);
8163 png_free(ping,text[0].key);
8164 png_free(ping,text);
8167 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
8168 const char *string, MagickBooleanType logging)
8181 ResetImageProfileIterator(image);
8183 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8185 profile=GetImageProfile(image,name);
8187 if (profile != (const StringInfo *) NULL)
8192 if (LocaleNCompare(name,string,11) == 0)
8194 if (logging != MagickFalse)
8195 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8196 " Found %s profile",name);
8198 ping_profile=CloneStringInfo(profile);
8199 data=GetStringInfoDatum(ping_profile),
8200 length=(png_uint_32) GetStringInfoLength(ping_profile);
8205 (void) WriteBlobMSBULong(image,length-5); /* data length */
8206 (void) WriteBlob(image,length-1,data+1);
8207 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
8208 ping_profile=DestroyStringInfo(ping_profile);
8212 name=GetNextImageProfile(image);
8218 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8219 const Quantum *p, const PixelInfo *q)
8224 value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8225 if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8226 return(MagickFalse);
8227 value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8228 if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8229 return(MagickFalse);
8230 value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8231 if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8232 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 *date,ExceptionInfo *exception)
8255 if (date != (const char *) NULL)
8257 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
8260 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8261 "Invalid date format specified for png:tIME","`%s'",
8265 ptime.year=(png_uint_16) year;
8266 ptime.month=(png_byte) month;
8267 ptime.day=(png_byte) day;
8268 ptime.hour=(png_byte) hour;
8269 ptime.minute=(png_byte) minute;
8270 ptime.second=(png_byte) second;
8275 png_convert_from_time_t(&ptime,ttime);
8277 png_set_tIME(ping,info,&ptime);
8281 /* Write one PNG image */
8282 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8283 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8315 ping_trans_alpha[256];
8343 ping_have_cheap_transparency,
8357 /* ping_exclude_EXIF, */
8361 /* ping_exclude_iTXt, */
8367 /* ping_exclude_tRNS, */
8370 ping_exclude_zCCP, /* hex-encoded iCCP */
8373 ping_preserve_colormap,
8375 ping_need_colortype_warning,
8383 *volatile pixel_info;
8402 ping_interlace_method,
8403 ping_compression_method,
8420 number_semitransparent,
8422 ping_pHYs_unit_type;
8425 ping_pHYs_x_resolution,
8426 ping_pHYs_y_resolution;
8428 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8429 " Enter WriteOnePNGImage()");
8431 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8432 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8433 if (image_info == (ImageInfo *) NULL)
8434 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8436 /* Define these outside of the following "if logging()" block so they will
8437 * show in debuggers.
8440 (void) ConcatenateMagickString(im_vers,
8441 MagickLibVersionText,MagickPathExtent);
8442 (void) ConcatenateMagickString(im_vers,
8443 MagickLibAddendum,MagickPathExtent);
8446 (void) ConcatenateMagickString(libpng_vers,
8447 PNG_LIBPNG_VER_STRING,32);
8449 (void) ConcatenateMagickString(libpng_runv,
8450 png_get_libpng_ver(NULL),32);
8453 (void) ConcatenateMagickString(zlib_vers,
8456 (void) ConcatenateMagickString(zlib_runv,
8459 if (logging != MagickFalse)
8461 (void) LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8463 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8465 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8467 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8470 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8472 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8474 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8479 /* Initialize some stuff */
8482 ping_interlace_method=0,
8483 ping_compression_method=0,
8484 ping_filter_method=0,
8487 ping_background.red = 0;
8488 ping_background.green = 0;
8489 ping_background.blue = 0;
8490 ping_background.gray = 0;
8491 ping_background.index = 0;
8493 ping_trans_color.red=0;
8494 ping_trans_color.green=0;
8495 ping_trans_color.blue=0;
8496 ping_trans_color.gray=0;
8498 ping_pHYs_unit_type = 0;
8499 ping_pHYs_x_resolution = 0;
8500 ping_pHYs_y_resolution = 0;
8502 ping_have_blob=MagickFalse;
8503 ping_have_cheap_transparency=MagickFalse;
8504 ping_have_color=MagickTrue;
8505 ping_have_non_bw=MagickTrue;
8506 ping_have_PLTE=MagickFalse;
8507 ping_have_bKGD=MagickFalse;
8508 ping_have_eXIf=MagickTrue;
8509 ping_have_iCCP=MagickFalse;
8510 ping_have_pHYs=MagickFalse;
8511 ping_have_sRGB=MagickFalse;
8512 ping_have_tRNS=MagickFalse;
8514 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8515 ping_exclude_caNv=mng_info->ping_exclude_caNv;
8516 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8517 ping_exclude_date=mng_info->ping_exclude_date;
8518 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8519 ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8520 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8521 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8522 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8523 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8524 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8525 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8526 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8527 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8528 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8529 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8530 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8531 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8533 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8534 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8535 ping_need_colortype_warning = MagickFalse;
8537 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8538 * i.e., eliminate the ICC profile and set image->rendering_intent.
8539 * Note that this will not involve any changes to the actual pixels
8540 * but merely passes information to applications that read the resulting
8543 * To do: recognize other variants of the sRGB profile, using the CRC to
8544 * verify all recognized variants including the 7 already known.
8546 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8548 * Use something other than image->rendering_intent to record the fact
8549 * that the sRGB profile was found.
8551 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8552 * profile. Record the Blackpoint Compensation, if any.
8554 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8562 ResetImageProfileIterator(image);
8563 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8565 profile=GetImageProfile(image,name);
8567 if (profile != (StringInfo *) NULL)
8569 if ((LocaleCompare(name,"ICC") == 0) ||
8570 (LocaleCompare(name,"ICM") == 0))
8585 length=(png_uint_32) GetStringInfoLength(profile);
8587 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8589 if (length == sRGB_info[icheck].len)
8593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8594 " Got a %lu-byte ICC profile (potentially sRGB)",
8595 (unsigned long) length);
8597 data=GetStringInfoDatum(profile);
8598 profile_crc=crc32(0,data,length);
8600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601 " with crc=%8x",(unsigned int) profile_crc);
8605 if (profile_crc == sRGB_info[icheck].crc)
8607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8608 " It is sRGB with rendering intent = %s",
8609 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8610 sRGB_info[icheck].intent));
8611 if (image->rendering_intent==UndefinedIntent)
8613 image->rendering_intent=
8614 Magick_RenderingIntent_from_PNG_RenderingIntent(
8615 sRGB_info[icheck].intent);
8617 ping_exclude_iCCP = MagickTrue;
8618 ping_exclude_zCCP = MagickTrue;
8619 ping_have_sRGB = MagickTrue;
8624 if (sRGB_info[icheck].len == 0)
8625 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8626 " Got %lu-byte ICC profile not recognized as sRGB",
8627 (unsigned long) length);
8630 name=GetNextImageProfile(image);
8635 number_semitransparent = 0;
8636 number_transparent = 0;
8638 if (logging != MagickFalse)
8640 if (image->storage_class == UndefinedClass)
8641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8642 " image->storage_class=UndefinedClass");
8643 if (image->storage_class == DirectClass)
8644 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8645 " image->storage_class=DirectClass");
8646 if (image->storage_class == PseudoClass)
8647 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8648 " image->storage_class=PseudoClass");
8649 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8650 " image->taint=MagickTrue":
8651 " image->taint=MagickFalse");
8654 if (image->storage_class == PseudoClass &&
8655 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8656 mng_info->write_png48 || mng_info->write_png64 ||
8657 (mng_info->write_png_colortype != 1 &&
8658 mng_info->write_png_colortype != 5)))
8660 (void) SyncImage(image,exception);
8661 image->storage_class = DirectClass;
8664 if (ping_preserve_colormap == MagickFalse)
8666 if (image->storage_class != PseudoClass && image->colormap != NULL)
8668 /* Free the bogus colormap; it can cause trouble later */
8669 if (logging != MagickFalse)
8670 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8671 " Freeing bogus colormap");
8672 (void) RelinquishMagickMemory(image->colormap);
8673 image->colormap=NULL;
8677 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8678 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8681 Sometimes we get PseudoClass images whose RGB values don't match
8682 the colors in the colormap. This code syncs the RGB values.
8684 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8685 (void) SyncImage(image,exception);
8687 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8688 if (image->depth > 8)
8690 if (logging != MagickFalse)
8691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8692 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8698 /* Respect the -depth option */
8699 if (image->depth < 4)
8704 if (image->depth > 2)
8706 /* Scale to 4-bit */
8707 LBR04PacketRGBO(image->background_color);
8709 for (y=0; y < (ssize_t) image->rows; y++)
8711 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8713 if (r == (Quantum *) NULL)
8716 for (x=0; x < (ssize_t) image->columns; x++)
8719 r+=GetPixelChannels(image);
8722 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8726 if (image->storage_class == PseudoClass && image->colormap != NULL)
8728 for (i=0; i < (ssize_t) image->colors; i++)
8730 LBR04PacketRGBO(image->colormap[i]);
8734 else if (image->depth > 1)
8736 /* Scale to 2-bit */
8737 LBR02PacketRGBO(image->background_color);
8739 for (y=0; y < (ssize_t) image->rows; y++)
8741 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8743 if (r == (Quantum *) NULL)
8746 for (x=0; x < (ssize_t) image->columns; x++)
8749 r+=GetPixelChannels(image);
8752 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8756 if (image->storage_class == PseudoClass && image->colormap != NULL)
8758 for (i=0; i < (ssize_t) image->colors; i++)
8760 LBR02PacketRGBO(image->colormap[i]);
8766 /* Scale to 1-bit */
8767 LBR01PacketRGBO(image->background_color);
8769 for (y=0; y < (ssize_t) image->rows; y++)
8771 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8773 if (r == (Quantum *) NULL)
8776 for (x=0; x < (ssize_t) image->columns; x++)
8779 r+=GetPixelChannels(image);
8782 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8786 if (image->storage_class == PseudoClass && image->colormap != NULL)
8788 for (i=0; i < (ssize_t) image->colors; i++)
8790 LBR01PacketRGBO(image->colormap[i]);
8796 /* To do: set to next higher multiple of 8 */
8797 if (image->depth < 8)
8800 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8801 /* PNG does not handle depths greater than 16 so reduce it even
8804 if (image->depth > 8)
8808 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8809 if (image->depth > 8)
8811 /* To do: fill low byte properly */
8815 if (image->depth == 16 && mng_info->write_png_depth != 16)
8816 if (mng_info->write_png8 ||
8817 LosslessReduceDepthOK(image,exception) != MagickFalse)
8821 image_colors = (int) image->colors;
8822 number_opaque = (int) image->colors;
8823 number_transparent = 0;
8824 number_semitransparent = 0;
8826 if (mng_info->write_png_colortype &&
8827 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8828 mng_info->write_png_colortype < 4 &&
8829 image->alpha_trait == UndefinedPixelTrait)))
8831 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8832 * are not going to need the result.
8834 if (mng_info->write_png_colortype == 1 ||
8835 mng_info->write_png_colortype == 5)
8836 ping_have_color=MagickFalse;
8838 if (image->alpha_trait != UndefinedPixelTrait)
8840 number_transparent = 2;
8841 number_semitransparent = 1;
8845 if (mng_info->write_png_colortype < 7)
8849 * Normally we run this just once, but in the case of writing PNG8
8850 * we reduce the transparency to binary and run again, then if there
8851 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8852 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8853 * palette. Then (To do) we take care of a final reduction that is only
8854 * needed if there are still 256 colors present and one of them has both
8855 * transparent and opaque instances.
8858 tried_332 = MagickFalse;
8859 tried_333 = MagickFalse;
8860 tried_444 = MagickFalse;
8865 * Sometimes we get DirectClass images that have 256 colors or fewer.
8866 * This code will build a colormap.
8868 * Also, sometimes we get PseudoClass images with an out-of-date
8869 * colormap. This code will replace the colormap with a new one.
8870 * Sometimes we get PseudoClass images that have more than 256 colors.
8871 * This code will delete the colormap and change the image to
8874 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8875 * even though it sometimes contains left-over non-opaque values.
8877 * Also we gather some information (number of opaque, transparent,
8878 * and semitransparent pixels, and whether the image has any non-gray
8879 * pixels or only black-and-white pixels) that we might need later.
8881 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8882 * we need to check for bogus non-opaque values, at least.
8890 semitransparent[260],
8893 register const Quantum
8900 if (logging != MagickFalse)
8901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902 " Enter BUILD_PALETTE:");
8904 if (logging != MagickFalse)
8906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8907 " image->columns=%.20g",(double) image->columns);
8908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8909 " image->rows=%.20g",(double) image->rows);
8910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8911 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8913 " image->depth=%.20g",(double) image->depth);
8915 if (image->storage_class == PseudoClass && image->colormap != NULL)
8917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8918 " Original colormap:");
8919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920 " i (red,green,blue,alpha)");
8922 for (i=0; i < 256; i++)
8924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8925 " %d (%d,%d,%d,%d)",
8927 (int) image->colormap[i].red,
8928 (int) image->colormap[i].green,
8929 (int) image->colormap[i].blue,
8930 (int) image->colormap[i].alpha);
8933 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8937 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938 " %d (%d,%d,%d,%d)",
8940 (int) image->colormap[i].red,
8941 (int) image->colormap[i].green,
8942 (int) image->colormap[i].blue,
8943 (int) image->colormap[i].alpha);
8948 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949 " image->colors=%d",(int) image->colors);
8951 if (image->colors == 0)
8952 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8953 " (zero means unknown)");
8955 if (ping_preserve_colormap == MagickFalse)
8956 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8957 " Regenerate the colormap");
8962 number_semitransparent = 0;
8963 number_transparent = 0;
8965 for (y=0; y < (ssize_t) image->rows; y++)
8967 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8969 if (q == (Quantum *) NULL)
8972 for (x=0; x < (ssize_t) image->columns; x++)
8974 if (image->alpha_trait == UndefinedPixelTrait ||
8975 GetPixelAlpha(image,q) == OpaqueAlpha)
8977 if (number_opaque < 259)
8979 if (number_opaque == 0)
8981 GetPixelInfoPixel(image, q, opaque);
8982 opaque[0].alpha=OpaqueAlpha;
8986 for (i=0; i< (ssize_t) number_opaque; i++)
8988 if (Magick_png_color_equal(image,q,opaque+i))
8992 if (i == (ssize_t) number_opaque && number_opaque < 259)
8995 GetPixelInfoPixel(image, q, opaque+i);
8996 opaque[i].alpha=OpaqueAlpha;
9000 else if (GetPixelAlpha(image,q) == TransparentAlpha)
9002 if (number_transparent < 259)
9004 if (number_transparent == 0)
9006 GetPixelInfoPixel(image, q, transparent);
9007 ping_trans_color.red=(unsigned short)
9008 GetPixelRed(image,q);
9009 ping_trans_color.green=(unsigned short)
9010 GetPixelGreen(image,q);
9011 ping_trans_color.blue=(unsigned short)
9012 GetPixelBlue(image,q);
9013 ping_trans_color.gray=(unsigned short)
9014 GetPixelGray(image,q);
9015 number_transparent = 1;
9018 for (i=0; i< (ssize_t) number_transparent; i++)
9020 if (Magick_png_color_equal(image,q,transparent+i))
9024 if (i == (ssize_t) number_transparent &&
9025 number_transparent < 259)
9027 number_transparent++;
9028 GetPixelInfoPixel(image,q,transparent+i);
9034 if (number_semitransparent < 259)
9036 if (number_semitransparent == 0)
9038 GetPixelInfoPixel(image,q,semitransparent);
9039 number_semitransparent = 1;
9042 for (i=0; i< (ssize_t) number_semitransparent; i++)
9044 if (Magick_png_color_equal(image,q,semitransparent+i)
9045 && GetPixelAlpha(image,q) ==
9046 semitransparent[i].alpha)
9050 if (i == (ssize_t) number_semitransparent &&
9051 number_semitransparent < 259)
9053 number_semitransparent++;
9054 GetPixelInfoPixel(image, q, semitransparent+i);
9058 q+=GetPixelChannels(image);
9062 if (mng_info->write_png8 == MagickFalse &&
9063 ping_exclude_bKGD == MagickFalse)
9065 /* Add the background color to the palette, if it
9066 * isn't already there.
9068 if (logging != MagickFalse)
9070 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9071 " Check colormap for background (%d,%d,%d)",
9072 (int) image->background_color.red,
9073 (int) image->background_color.green,
9074 (int) image->background_color.blue);
9076 for (i=0; i<number_opaque; i++)
9078 if (opaque[i].red == image->background_color.red &&
9079 opaque[i].green == image->background_color.green &&
9080 opaque[i].blue == image->background_color.blue)
9083 if (number_opaque < 259 && i == number_opaque)
9085 opaque[i] = image->background_color;
9086 ping_background.index = i;
9088 if (logging != MagickFalse)
9090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9091 " background_color index is %d",(int) i);
9095 else if (logging != MagickFalse)
9096 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9097 " No room in the colormap to add background color");
9100 image_colors=number_opaque+number_transparent+number_semitransparent;
9102 if (logging != MagickFalse)
9104 if (image_colors > 256)
9105 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106 " image has more than 256 colors");
9109 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9110 " image has %d colors",image_colors);
9113 if (ping_preserve_colormap != MagickFalse)
9116 if (mng_info->write_png_colortype != 7) /* We won't need this info */
9118 ping_have_color=MagickFalse;
9119 ping_have_non_bw=MagickFalse;
9121 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9124 "incompatible colorspace");
9125 ping_have_color=MagickTrue;
9126 ping_have_non_bw=MagickTrue;
9129 if(image_colors > 256)
9131 for (y=0; y < (ssize_t) image->rows; y++)
9133 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9135 if (q == (Quantum *) NULL)
9139 for (x=0; x < (ssize_t) image->columns; x++)
9141 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
9142 GetPixelRed(image,s) != GetPixelBlue(image,s))
9144 ping_have_color=MagickTrue;
9145 ping_have_non_bw=MagickTrue;
9148 s+=GetPixelChannels(image);
9151 if (ping_have_color != MagickFalse)
9154 /* Worst case is black-and-white; we are looking at every
9158 if (ping_have_non_bw == MagickFalse)
9161 for (x=0; x < (ssize_t) image->columns; x++)
9163 if (GetPixelRed(image,s) != 0 &&
9164 GetPixelRed(image,s) != QuantumRange)
9166 ping_have_non_bw=MagickTrue;
9169 s+=GetPixelChannels(image);
9176 if (image_colors < 257)
9182 * Initialize image colormap.
9185 if (logging != MagickFalse)
9186 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9187 " Sort the new colormap");
9189 /* Sort palette, transparent first */;
9193 for (i=0; i<number_transparent; i++)
9194 colormap[n++] = transparent[i];
9196 for (i=0; i<number_semitransparent; i++)
9197 colormap[n++] = semitransparent[i];
9199 for (i=0; i<number_opaque; i++)
9200 colormap[n++] = opaque[i];
9202 ping_background.index +=
9203 (number_transparent + number_semitransparent);
9205 /* image_colors < 257; search the colormap instead of the pixels
9206 * to get ping_have_color and ping_have_non_bw
9210 if (ping_have_color == MagickFalse)
9212 if (colormap[i].red != colormap[i].green ||
9213 colormap[i].red != colormap[i].blue)
9215 ping_have_color=MagickTrue;
9216 ping_have_non_bw=MagickTrue;
9221 if (ping_have_non_bw == MagickFalse)
9223 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9224 ping_have_non_bw=MagickTrue;
9228 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9229 (number_transparent == 0 && number_semitransparent == 0)) &&
9230 (((mng_info->write_png_colortype-1) ==
9231 PNG_COLOR_TYPE_PALETTE) ||
9232 (mng_info->write_png_colortype == 0)))
9234 if (logging != MagickFalse)
9236 if (n != (ssize_t) image_colors)
9237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9238 " image_colors (%d) and n (%d) don't match",
9241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9242 " AcquireImageColormap");
9245 image->colors = image_colors;
9247 if (AcquireImageColormap(image,image_colors,exception) ==
9249 ThrowWriterException(ResourceLimitError,
9250 "MemoryAllocationFailed");
9252 for (i=0; i< (ssize_t) image_colors; i++)
9253 image->colormap[i] = colormap[i];
9255 if (logging != MagickFalse)
9257 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9258 " image->colors=%d (%d)",
9259 (int) image->colors, image_colors);
9261 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9262 " Update the pixel indexes");
9265 /* Sync the pixel indices with the new colormap */
9267 for (y=0; y < (ssize_t) image->rows; y++)
9269 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9271 if (q == (Quantum *) NULL)
9274 for (x=0; x < (ssize_t) image->columns; x++)
9276 for (i=0; i< (ssize_t) image_colors; i++)
9278 if ((image->alpha_trait == UndefinedPixelTrait ||
9279 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9280 image->colormap[i].red == GetPixelRed(image,q) &&
9281 image->colormap[i].green == GetPixelGreen(image,q) &&
9282 image->colormap[i].blue == GetPixelBlue(image,q))
9284 SetPixelIndex(image,i,q);
9288 q+=GetPixelChannels(image);
9291 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9297 if (logging != MagickFalse)
9299 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9300 " image->colors=%d", (int) image->colors);
9302 if (image->colormap != NULL)
9304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9305 " i (red,green,blue,alpha)");
9307 for (i=0; i < (ssize_t) image->colors; i++)
9309 if (i < 300 || i >= (ssize_t) image->colors - 10)
9311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9312 " %d (%d,%d,%d,%d)",
9314 (int) image->colormap[i].red,
9315 (int) image->colormap[i].green,
9316 (int) image->colormap[i].blue,
9317 (int) image->colormap[i].alpha);
9322 if (number_transparent < 257)
9323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9324 " number_transparent = %d",
9325 number_transparent);
9328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9329 " number_transparent > 256");
9331 if (number_opaque < 257)
9332 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9333 " number_opaque = %d",
9337 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9338 " number_opaque > 256");
9340 if (number_semitransparent < 257)
9341 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9342 " number_semitransparent = %d",
9343 number_semitransparent);
9346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9347 " number_semitransparent > 256");
9349 if (ping_have_non_bw == MagickFalse)
9350 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9351 " All pixels and the background are black or white");
9353 else if (ping_have_color == MagickFalse)
9354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9355 " All pixels and the background are gray");
9358 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9359 " At least one pixel or the background is non-gray");
9361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9362 " Exit BUILD_PALETTE:");
9365 if (mng_info->write_png8 == MagickFalse)
9368 /* Make any reductions necessary for the PNG8 format */
9369 if (image_colors <= 256 &&
9370 image_colors != 0 && image->colormap != NULL &&
9371 number_semitransparent == 0 &&
9372 number_transparent <= 1)
9375 /* PNG8 can't have semitransparent colors so we threshold the
9376 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9377 * transparent color so if more than one is transparent we merge
9378 * them into image->background_color.
9380 if (number_semitransparent != 0 || number_transparent > 1)
9382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9383 " Thresholding the alpha channel to binary");
9385 for (y=0; y < (ssize_t) image->rows; y++)
9387 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9389 if (r == (Quantum *) NULL)
9392 for (x=0; x < (ssize_t) image->columns; x++)
9394 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9396 SetPixelViaPixelInfo(image,&image->background_color,r);
9397 SetPixelAlpha(image,TransparentAlpha,r);
9400 SetPixelAlpha(image,OpaqueAlpha,r);
9401 r+=GetPixelChannels(image);
9404 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9407 if (image_colors != 0 && image_colors <= 256 &&
9408 image->colormap != NULL)
9409 for (i=0; i<image_colors; i++)
9410 image->colormap[i].alpha =
9411 (image->colormap[i].alpha > TransparentAlpha/2 ?
9412 TransparentAlpha : OpaqueAlpha);
9417 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9418 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9419 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9422 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9424 if (logging != MagickFalse)
9425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9426 " Quantizing the background color to 4-4-4");
9428 tried_444 = MagickTrue;
9430 LBR04PacketRGB(image->background_color);
9432 if (logging != MagickFalse)
9433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9434 " Quantizing the pixel colors to 4-4-4");
9436 if (image->colormap == NULL)
9438 for (y=0; y < (ssize_t) image->rows; y++)
9440 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9442 if (r == (Quantum *) NULL)
9445 for (x=0; x < (ssize_t) image->columns; x++)
9447 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9449 r+=GetPixelChannels(image);
9452 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9457 else /* Should not reach this; colormap already exists and
9460 if (logging != MagickFalse)
9461 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9462 " Quantizing the colormap to 4-4-4");
9464 for (i=0; i<image_colors; i++)
9466 LBR04PacketRGB(image->colormap[i]);
9472 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9474 if (logging != MagickFalse)
9475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9476 " Quantizing the background color to 3-3-3");
9478 tried_333 = MagickTrue;
9480 LBR03PacketRGB(image->background_color);
9482 if (logging != MagickFalse)
9483 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9484 " Quantizing the pixel colors to 3-3-3-1");
9486 if (image->colormap == NULL)
9488 for (y=0; y < (ssize_t) image->rows; y++)
9490 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9492 if (r == (Quantum *) NULL)
9495 for (x=0; x < (ssize_t) image->columns; x++)
9497 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9499 r+=GetPixelChannels(image);
9502 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9507 else /* Should not reach this; colormap already exists and
9510 if (logging != MagickFalse)
9511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9512 " Quantizing the colormap to 3-3-3-1");
9513 for (i=0; i<image_colors; i++)
9515 LBR03PacketRGB(image->colormap[i]);
9521 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9523 if (logging != MagickFalse)
9524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9525 " Quantizing the background color to 3-3-2");
9527 tried_332 = MagickTrue;
9529 /* Red and green were already done so we only quantize the blue
9533 LBR02PacketBlue(image->background_color);
9535 if (logging != MagickFalse)
9536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9537 " Quantizing the pixel colors to 3-3-2-1");
9539 if (image->colormap == NULL)
9541 for (y=0; y < (ssize_t) image->rows; y++)
9543 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9545 if (r == (Quantum *) NULL)
9548 for (x=0; x < (ssize_t) image->columns; x++)
9550 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9552 r+=GetPixelChannels(image);
9555 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9560 else /* Should not reach this; colormap already exists and
9563 if (logging != MagickFalse)
9564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9565 " Quantizing the colormap to 3-3-2-1");
9566 for (i=0; i<image_colors; i++)
9568 LBR02PacketBlue(image->colormap[i]);
9574 if (image_colors == 0 || image_colors > 256)
9576 /* Take care of special case with 256 opaque colors + 1 transparent
9577 * color. We don't need to quantize to 2-3-2-1; we only need to
9578 * eliminate one color, so we'll merge the two darkest red
9579 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9581 if (logging != MagickFalse)
9582 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9583 " Merging two dark red background colors to 3-3-2-1");
9585 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9586 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9587 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9589 image->background_color.red=ScaleCharToQuantum(0x24);
9592 if (logging != MagickFalse)
9593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9594 " Merging two dark red pixel colors to 3-3-2-1");
9596 if (image->colormap == NULL)
9598 for (y=0; y < (ssize_t) image->rows; y++)
9600 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9602 if (r == (Quantum *) NULL)
9605 for (x=0; x < (ssize_t) image->columns; x++)
9607 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9608 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9609 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9610 GetPixelAlpha(image,r) == OpaqueAlpha)
9612 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9614 r+=GetPixelChannels(image);
9617 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9625 for (i=0; i<image_colors; i++)
9627 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9628 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9629 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9631 image->colormap[i].red=ScaleCharToQuantum(0x24);
9638 /* END OF BUILD_PALETTE */
9640 /* If we are excluding the tRNS chunk and there is transparency,
9641 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9644 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9645 (number_transparent != 0 || number_semitransparent != 0))
9647 unsigned int colortype=mng_info->write_png_colortype;
9649 if (ping_have_color == MagickFalse)
9650 mng_info->write_png_colortype = 5;
9653 mng_info->write_png_colortype = 7;
9655 if (colortype != 0 &&
9656 mng_info->write_png_colortype != colortype)
9657 ping_need_colortype_warning=MagickTrue;
9661 /* See if cheap transparency is possible. It is only possible
9662 * when there is a single transparent color, no semitransparent
9663 * color, and no opaque color that has the same RGB components
9664 * as the transparent color. We only need this information if
9665 * we are writing a PNG with colortype 0 or 2, and we have not
9666 * excluded the tRNS chunk.
9668 if (number_transparent == 1 &&
9669 mng_info->write_png_colortype < 4)
9671 ping_have_cheap_transparency = MagickTrue;
9673 if (number_semitransparent != 0)
9674 ping_have_cheap_transparency = MagickFalse;
9676 else if (image_colors == 0 || image_colors > 256 ||
9677 image->colormap == NULL)
9679 register const Quantum
9682 for (y=0; y < (ssize_t) image->rows; y++)
9684 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9686 if (q == (Quantum *) NULL)
9689 for (x=0; x < (ssize_t) image->columns; x++)
9691 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9692 (unsigned short) GetPixelRed(image,q) ==
9693 ping_trans_color.red &&
9694 (unsigned short) GetPixelGreen(image,q) ==
9695 ping_trans_color.green &&
9696 (unsigned short) GetPixelBlue(image,q) ==
9697 ping_trans_color.blue)
9699 ping_have_cheap_transparency = MagickFalse;
9703 q+=GetPixelChannels(image);
9706 if (ping_have_cheap_transparency == MagickFalse)
9712 /* Assuming that image->colormap[0] is the one transparent color
9713 * and that all others are opaque.
9715 if (image_colors > 1)
9716 for (i=1; i<image_colors; i++)
9717 if (image->colormap[i].red == image->colormap[0].red &&
9718 image->colormap[i].green == image->colormap[0].green &&
9719 image->colormap[i].blue == image->colormap[0].blue)
9721 ping_have_cheap_transparency = MagickFalse;
9726 if (logging != MagickFalse)
9728 if (ping_have_cheap_transparency == MagickFalse)
9729 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9730 " Cheap transparency is not possible.");
9733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9734 " Cheap transparency is possible.");
9738 ping_have_cheap_transparency = MagickFalse;
9740 image_depth=image->depth;
9742 quantum_info = (QuantumInfo *) NULL;
9744 image_colors=(int) image->colors;
9745 image_matte=image->alpha_trait !=
9746 UndefinedPixelTrait ? MagickTrue : MagickFalse;
9748 if (mng_info->write_png_colortype < 5)
9749 mng_info->IsPalette=image->storage_class == PseudoClass &&
9750 image_colors <= 256 && image->colormap != NULL;
9752 mng_info->IsPalette = MagickFalse;
9754 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9755 (image->colors == 0 || image->colormap == NULL))
9757 image_info=DestroyImageInfo(image_info);
9758 image=DestroyImage(image);
9759 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9760 "Cannot write PNG8 or color-type 3; colormap is NULL",
9761 "`%s'",IMimage->filename);
9762 return(MagickFalse);
9766 Allocate the PNG structures
9768 #ifdef PNG_USER_MEM_SUPPORTED
9769 error_info.image=image;
9770 error_info.exception=exception;
9771 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9772 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9773 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9776 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9777 MagickPNGErrorHandler,MagickPNGWarningHandler);
9780 if (ping == (png_struct *) NULL)
9781 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9783 ping_info=png_create_info_struct(ping);
9785 if (ping_info == (png_info *) NULL)
9787 png_destroy_write_struct(&ping,(png_info **) NULL);
9788 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9791 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9792 pixel_info=(MemoryInfo *) NULL;
9794 if (setjmp(png_jmpbuf(ping)))
9800 if (image_info->verbose)
9801 (void) printf("PNG write has failed.\n");
9803 png_destroy_write_struct(&ping,&ping_info);
9804 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9805 UnlockSemaphoreInfo(ping_semaphore);
9808 if (pixel_info != (MemoryInfo *) NULL)
9809 pixel_info=RelinquishVirtualMemory(pixel_info);
9811 if (quantum_info != (QuantumInfo *) NULL)
9812 quantum_info=DestroyQuantumInfo(quantum_info);
9814 if (ping_have_blob != MagickFalse)
9815 (void) CloseBlob(image);
9816 image_info=DestroyImageInfo(image_info);
9817 image=DestroyImage(image);
9818 return(MagickFalse);
9821 /* { For navigation to end of SETJMP-protected block. Within this
9822 * block, use png_error() instead of Throwing an Exception, to ensure
9823 * that libpng is able to clean up, and that the semaphore is unlocked.
9826 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9827 LockSemaphoreInfo(ping_semaphore);
9830 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9831 /* Allow benign errors */
9832 png_set_benign_errors(ping, 1);
9835 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9836 /* Reject images with too many rows or columns */
9837 png_set_user_limits(ping,
9838 (png_uint_32) MagickMin(0x7fffffffL,
9839 GetMagickResourceLimit(WidthResource)),
9840 (png_uint_32) MagickMin(0x7fffffffL,
9841 GetMagickResourceLimit(HeightResource)));
9842 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9845 Prepare PNG for writing.
9848 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9849 if (mng_info->write_mng)
9851 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9852 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9853 /* Disable new libpng-1.5.10 feature when writing a MNG because
9854 * zero-length PLTE is OK
9856 png_set_check_for_invalid_index (ping, 0);
9861 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9862 if (mng_info->write_mng)
9863 png_permit_empty_plte(ping,MagickTrue);
9870 ping_width=(png_uint_32) image->columns;
9871 ping_height=(png_uint_32) image->rows;
9873 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9876 if (mng_info->write_png48 || mng_info->write_png64)
9879 if (mng_info->write_png_depth != 0)
9880 image_depth=mng_info->write_png_depth;
9882 /* Adjust requested depth to next higher valid depth if necessary */
9883 if (image_depth > 8)
9886 if ((image_depth > 4) && (image_depth < 8))
9889 if (image_depth == 3)
9892 if (logging != MagickFalse)
9894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9895 " width=%.20g",(double) ping_width);
9896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9897 " height=%.20g",(double) ping_height);
9898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9899 " image_matte=%.20g",(double) image->alpha_trait);
9900 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9901 " image->depth=%.20g",(double) image->depth);
9902 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9903 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9906 save_image_depth=image_depth;
9907 ping_bit_depth=(png_byte) save_image_depth;
9910 #if defined(PNG_pHYs_SUPPORTED)
9911 if (ping_exclude_pHYs == MagickFalse)
9913 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9914 (!mng_info->write_mng || !mng_info->equal_physs))
9916 if (logging != MagickFalse)
9917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9918 " Setting up pHYs chunk");
9920 if (image->units == PixelsPerInchResolution)
9922 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9923 ping_pHYs_x_resolution=
9924 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9925 ping_pHYs_y_resolution=
9926 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9929 else if (image->units == PixelsPerCentimeterResolution)
9931 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9932 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9933 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9938 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9939 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9940 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9943 if (logging != MagickFalse)
9944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9945 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9946 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9947 (int) ping_pHYs_unit_type);
9948 ping_have_pHYs = MagickTrue;
9953 if (ping_exclude_bKGD == MagickFalse)
9955 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9961 if (ping_bit_depth == 8)
9964 if (ping_bit_depth == 4)
9967 if (ping_bit_depth == 2)
9970 if (ping_bit_depth == 1)
9973 ping_background.red=(png_uint_16)
9974 (ScaleQuantumToShort(image->background_color.red) & mask);
9976 ping_background.green=(png_uint_16)
9977 (ScaleQuantumToShort(image->background_color.green) & mask);
9979 ping_background.blue=(png_uint_16)
9980 (ScaleQuantumToShort(image->background_color.blue) & mask);
9982 ping_background.gray=(png_uint_16) ping_background.green;
9985 if (logging != MagickFalse)
9987 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9988 " Setting up bKGD chunk (1)");
9989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9990 " background_color index is %d",
9991 (int) ping_background.index);
9993 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9994 " ping_bit_depth=%d",ping_bit_depth);
9997 ping_have_bKGD = MagickTrue;
10001 Select the color type.
10006 if (mng_info->IsPalette && mng_info->write_png8)
10008 /* To do: make this a function cause it's used twice, except
10009 for reducing the sample depth from 8. */
10011 number_colors=image_colors;
10013 ping_have_tRNS=MagickFalse;
10018 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10020 if (logging != MagickFalse)
10021 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10022 " Setting up PLTE chunk with %d colors (%d)",
10023 number_colors, image_colors);
10025 for (i=0; i < (ssize_t) number_colors; i++)
10027 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10028 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10029 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10030 if (logging != MagickFalse)
10031 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10032 #if MAGICKCORE_QUANTUM_DEPTH == 8
10033 " %3ld (%3d,%3d,%3d)",
10035 " %5ld (%5d,%5d,%5d)",
10037 (long) i,palette[i].red,palette[i].green,palette[i].blue);
10041 ping_have_PLTE=MagickTrue;
10042 image_depth=ping_bit_depth;
10045 if (matte != MagickFalse)
10048 Identify which colormap entry is transparent.
10050 assert(number_colors <= 256);
10051 assert(image->colormap != NULL);
10053 for (i=0; i < (ssize_t) number_transparent; i++)
10054 ping_trans_alpha[i]=0;
10057 ping_num_trans=(unsigned short) (number_transparent +
10058 number_semitransparent);
10060 if (ping_num_trans == 0)
10061 ping_have_tRNS=MagickFalse;
10064 ping_have_tRNS=MagickTrue;
10067 if (ping_exclude_bKGD == MagickFalse)
10070 * Identify which colormap entry is the background color.
10073 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10074 if (IsPNGColorEqual(ping_background,image->colormap[i]))
10077 ping_background.index=(png_byte) i;
10079 if (logging != MagickFalse)
10081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10082 " background_color index is %d",
10083 (int) ping_background.index);
10086 } /* end of write_png8 */
10088 else if (mng_info->write_png_colortype == 1)
10090 image_matte=MagickFalse;
10091 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10094 else if (mng_info->write_png24 || mng_info->write_png48 ||
10095 mng_info->write_png_colortype == 3)
10097 image_matte=MagickFalse;
10098 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10101 else if (mng_info->write_png32 || mng_info->write_png64 ||
10102 mng_info->write_png_colortype == 7)
10104 image_matte=MagickTrue;
10105 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10108 else /* mng_info->write_pngNN not specified */
10110 image_depth=ping_bit_depth;
10112 if (mng_info->write_png_colortype != 0)
10114 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10116 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10117 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10118 image_matte=MagickTrue;
10121 image_matte=MagickFalse;
10123 if (logging != MagickFalse)
10124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10125 " PNG colortype %d was specified:",(int) ping_color_type);
10128 else /* write_png_colortype not specified */
10130 if (logging != MagickFalse)
10131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10132 " Selecting PNG colortype:");
10134 ping_color_type=(png_byte) ((matte != MagickFalse)?
10135 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
10137 if (image_info->type == TrueColorType)
10139 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10140 image_matte=MagickFalse;
10143 if (image_info->type == TrueColorAlphaType)
10145 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10146 image_matte=MagickTrue;
10149 if (image_info->type == PaletteType ||
10150 image_info->type == PaletteAlphaType)
10151 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10153 if (mng_info->write_png_colortype == 0 &&
10154 image_info->type == UndefinedType)
10156 if (ping_have_color == MagickFalse)
10158 if (image_matte == MagickFalse)
10160 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10161 image_matte=MagickFalse;
10166 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10167 image_matte=MagickTrue;
10172 if (image_matte == MagickFalse)
10174 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10175 image_matte=MagickFalse;
10180 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10181 image_matte=MagickTrue;
10188 if (logging != MagickFalse)
10189 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10190 " Selected PNG colortype=%d",ping_color_type);
10192 if (ping_bit_depth < 8)
10194 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10195 ping_color_type == PNG_COLOR_TYPE_RGB ||
10196 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10200 old_bit_depth=ping_bit_depth;
10202 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10204 if (image->alpha_trait == UndefinedPixelTrait &&
10205 ping_have_non_bw == MagickFalse)
10209 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10214 if (image->colors == 0)
10217 png_error(ping,"image has 0 colors");
10220 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10221 ping_bit_depth <<= 1;
10224 if (logging != MagickFalse)
10226 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10227 " Number of colors: %.20g",(double) image_colors);
10229 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10230 " Tentative PNG bit depth: %d",ping_bit_depth);
10233 if (ping_bit_depth < (int) mng_info->write_png_depth)
10234 ping_bit_depth = mng_info->write_png_depth;
10237 image_depth=ping_bit_depth;
10239 if (logging != MagickFalse)
10241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10242 " Tentative PNG color type: %s (%.20g)",
10243 PngColorTypeToString(ping_color_type),
10244 (double) ping_color_type);
10246 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10247 " image_info->type: %.20g",(double) image_info->type);
10249 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250 " image_depth: %.20g",(double) image_depth);
10252 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10254 " image->depth: %.20g",(double) image->depth);
10256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10257 " ping_bit_depth: %.20g",(double) ping_bit_depth);
10260 if (matte != MagickFalse)
10262 if (mng_info->IsPalette)
10264 if (mng_info->write_png_colortype == 0)
10266 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10268 if (ping_have_color != MagickFalse)
10269 ping_color_type=PNG_COLOR_TYPE_RGBA;
10273 * Determine if there is any transparent color.
10275 if (number_transparent + number_semitransparent == 0)
10278 No transparent pixels are present. Change 4 or 6 to 0 or 2.
10281 image_matte=MagickFalse;
10283 if (mng_info->write_png_colortype == 0)
10284 ping_color_type&=0x03;
10294 if (ping_bit_depth == 8)
10297 if (ping_bit_depth == 4)
10300 if (ping_bit_depth == 2)
10303 if (ping_bit_depth == 1)
10306 ping_trans_color.red=(png_uint_16)
10307 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10309 ping_trans_color.green=(png_uint_16)
10310 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10312 ping_trans_color.blue=(png_uint_16)
10313 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10315 ping_trans_color.gray=(png_uint_16)
10316 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10317 image->colormap)) & mask);
10319 ping_trans_color.index=(png_byte) 0;
10321 ping_have_tRNS=MagickTrue;
10324 if (ping_have_tRNS != MagickFalse)
10327 * Determine if there is one and only one transparent color
10328 * and if so if it is fully transparent.
10330 if (ping_have_cheap_transparency == MagickFalse)
10331 ping_have_tRNS=MagickFalse;
10334 if (ping_have_tRNS != MagickFalse)
10336 if (mng_info->write_png_colortype == 0)
10337 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10339 if (image_depth == 8)
10341 ping_trans_color.red&=0xff;
10342 ping_trans_color.green&=0xff;
10343 ping_trans_color.blue&=0xff;
10344 ping_trans_color.gray&=0xff;
10350 if (image_depth == 8)
10352 ping_trans_color.red&=0xff;
10353 ping_trans_color.green&=0xff;
10354 ping_trans_color.blue&=0xff;
10355 ping_trans_color.gray&=0xff;
10362 if (ping_have_tRNS != MagickFalse)
10363 image_matte=MagickFalse;
10365 if ((mng_info->IsPalette) &&
10366 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10367 ping_have_color == MagickFalse &&
10368 (image_matte == MagickFalse || image_depth >= 8))
10372 if (image_matte != MagickFalse)
10373 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10375 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10377 ping_color_type=PNG_COLOR_TYPE_GRAY;
10379 if (save_image_depth == 16 && image_depth == 8)
10381 if (logging != MagickFalse)
10383 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10384 " Scaling ping_trans_color (0)");
10386 ping_trans_color.gray*=0x0101;
10390 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10391 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10393 if ((image_colors == 0) ||
10394 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10395 image_colors=(int) (one << image_depth);
10397 if (image_depth > 8)
10403 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10405 if(!mng_info->write_png_depth)
10409 while ((int) (one << ping_bit_depth)
10410 < (ssize_t) image_colors)
10411 ping_bit_depth <<= 1;
10415 else if (ping_color_type ==
10416 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10417 mng_info->IsPalette)
10419 /* Check if grayscale is reducible */
10422 depth_4_ok=MagickTrue,
10423 depth_2_ok=MagickTrue,
10424 depth_1_ok=MagickTrue;
10426 for (i=0; i < (ssize_t) image_colors; i++)
10431 intensity=ScaleQuantumToChar(image->colormap[i].red);
10433 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10434 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10435 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10436 depth_2_ok=depth_1_ok=MagickFalse;
10437 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10438 depth_1_ok=MagickFalse;
10441 if (depth_1_ok && mng_info->write_png_depth <= 1)
10444 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10447 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10452 image_depth=ping_bit_depth;
10457 if (mng_info->IsPalette)
10459 number_colors=image_colors;
10461 if (image_depth <= 8)
10466 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10468 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10470 for (i=0; i < (ssize_t) number_colors; i++)
10472 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10474 ScaleQuantumToChar(image->colormap[i].green);
10475 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10478 if (logging != MagickFalse)
10479 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10480 " Setting up PLTE chunk with %d colors",
10483 ping_have_PLTE=MagickTrue;
10486 /* color_type is PNG_COLOR_TYPE_PALETTE */
10487 if (mng_info->write_png_depth == 0)
10495 while ((one << ping_bit_depth) < (size_t) number_colors)
10496 ping_bit_depth <<= 1;
10501 if (matte != MagickFalse)
10504 * Set up trans_colors array.
10506 assert(number_colors <= 256);
10508 ping_num_trans=(unsigned short) (number_transparent +
10509 number_semitransparent);
10511 if (ping_num_trans == 0)
10512 ping_have_tRNS=MagickFalse;
10516 if (logging != MagickFalse)
10518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10519 " Scaling ping_trans_color (1)");
10521 ping_have_tRNS=MagickTrue;
10523 for (i=0; i < ping_num_trans; i++)
10525 ping_trans_alpha[i]= (png_byte)
10526 ScaleQuantumToChar(image->colormap[i].alpha);
10536 if (image_depth < 8)
10539 if ((save_image_depth == 16) && (image_depth == 8))
10541 if (logging != MagickFalse)
10543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10544 " Scaling ping_trans_color from (%d,%d,%d)",
10545 (int) ping_trans_color.red,
10546 (int) ping_trans_color.green,
10547 (int) ping_trans_color.blue);
10550 ping_trans_color.red*=0x0101;
10551 ping_trans_color.green*=0x0101;
10552 ping_trans_color.blue*=0x0101;
10553 ping_trans_color.gray*=0x0101;
10555 if (logging != MagickFalse)
10557 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10559 (int) ping_trans_color.red,
10560 (int) ping_trans_color.green,
10561 (int) ping_trans_color.blue);
10566 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10567 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10570 Adjust background and transparency samples in sub-8-bit grayscale files.
10572 if (ping_bit_depth < 8 && ping_color_type ==
10573 PNG_COLOR_TYPE_GRAY)
10581 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10583 if (ping_exclude_bKGD == MagickFalse)
10586 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10587 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10588 &image->background_color))) +.5)));
10590 if (logging != MagickFalse)
10591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10592 " Setting up bKGD chunk (2)");
10593 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10594 " background_color index is %d",
10595 (int) ping_background.index);
10597 ping_have_bKGD = MagickTrue;
10600 if (logging != MagickFalse)
10601 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10602 " Scaling ping_trans_color.gray from %d",
10603 (int)ping_trans_color.gray);
10605 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10606 ping_trans_color.gray)+.5);
10608 if (logging != MagickFalse)
10609 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610 " to %d", (int)ping_trans_color.gray);
10613 if (ping_exclude_bKGD == MagickFalse)
10615 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10618 Identify which colormap entry is the background color.
10621 number_colors=image_colors;
10623 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10624 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10627 ping_background.index=(png_byte) i;
10629 if (logging != MagickFalse)
10631 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10632 " Setting up bKGD chunk with index=%d",(int) i);
10635 if (i < (ssize_t) number_colors)
10637 ping_have_bKGD = MagickTrue;
10639 if (logging != MagickFalse)
10641 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10642 " background =(%d,%d,%d)",
10643 (int) ping_background.red,
10644 (int) ping_background.green,
10645 (int) ping_background.blue);
10649 else /* Can't happen */
10651 if (logging != MagickFalse)
10652 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10653 " No room in PLTE to add bKGD color");
10654 ping_have_bKGD = MagickFalse;
10659 if (logging != MagickFalse)
10660 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10661 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10664 Initialize compression level and filtering.
10666 if (logging != MagickFalse)
10668 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10669 " Setting up deflate compression");
10671 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10672 " Compression buffer size: 32768");
10675 png_set_compression_buffer_size(ping,32768L);
10677 if (logging != MagickFalse)
10678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679 " Compression mem level: 9");
10681 png_set_compression_mem_level(ping, 9);
10683 /* Untangle the "-quality" setting:
10685 Undefined is 0; the default is used.
10690 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10691 zlib default compression level
10693 1-9: the zlib compression level
10697 0-4: the PNG filter method
10699 5: libpng adaptive filtering if compression level > 5
10700 libpng filter type "none" if compression level <= 5
10701 or if image is grayscale or palette
10703 6: libpng adaptive filtering
10705 7: "LOCO" filtering (intrapixel differing) if writing
10706 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10707 and earlier because of a missing "else".
10709 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10710 filtering. Unused prior to IM-6.7.0-10, was same as 6
10712 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10713 Unused prior to IM-6.7.0-10, was same as 6
10715 Note that using the -quality option, not all combinations of
10716 PNG filter type, zlib compression level, and zlib compression
10717 strategy are possible. This will be addressed soon in a
10718 release that accomodates "-define png:compression-strategy", etc.
10722 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10723 image_info->quality;
10727 if (mng_info->write_png_compression_strategy == 0)
10728 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10731 else if (mng_info->write_png_compression_level == 0)
10736 level=(int) MagickMin((ssize_t) quality/10,9);
10738 mng_info->write_png_compression_level = level+1;
10741 if (mng_info->write_png_compression_strategy == 0)
10743 if ((quality %10) == 8 || (quality %10) == 9)
10744 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10745 mng_info->write_png_compression_strategy=Z_RLE+1;
10747 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10751 if (mng_info->write_png_compression_filter == 0)
10752 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10754 if (logging != MagickFalse)
10756 if (mng_info->write_png_compression_level)
10757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10758 " Compression level: %d",
10759 (int) mng_info->write_png_compression_level-1);
10761 if (mng_info->write_png_compression_strategy)
10762 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10763 " Compression strategy: %d",
10764 (int) mng_info->write_png_compression_strategy-1);
10766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10767 " Setting up filtering");
10769 if (mng_info->write_png_compression_filter == 6)
10770 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10771 " Base filter method: ADAPTIVE");
10772 else if (mng_info->write_png_compression_filter == 0 ||
10773 mng_info->write_png_compression_filter == 1)
10774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10775 " Base filter method: NONE");
10777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10778 " Base filter method: %d",
10779 (int) mng_info->write_png_compression_filter-1);
10782 if (mng_info->write_png_compression_level != 0)
10783 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10785 if (mng_info->write_png_compression_filter == 6)
10787 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10788 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10790 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10792 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10794 else if (mng_info->write_png_compression_filter == 7 ||
10795 mng_info->write_png_compression_filter == 10)
10796 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10798 else if (mng_info->write_png_compression_filter == 8)
10800 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10801 if (mng_info->write_mng)
10803 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10804 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10805 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10808 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10811 else if (mng_info->write_png_compression_filter == 9)
10812 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10814 else if (mng_info->write_png_compression_filter != 0)
10815 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10816 mng_info->write_png_compression_filter-1);
10818 if (mng_info->write_png_compression_strategy != 0)
10819 png_set_compression_strategy(ping,
10820 mng_info->write_png_compression_strategy-1);
10822 ping_interlace_method=image_info->interlace != NoInterlace;
10824 if (mng_info->write_mng)
10825 png_set_sig_bytes(ping,8);
10827 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10829 if (mng_info->write_png_colortype != 0)
10831 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10832 if (ping_have_color != MagickFalse)
10834 ping_color_type = PNG_COLOR_TYPE_RGB;
10836 if (ping_bit_depth < 8)
10840 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10841 if (ping_have_color != MagickFalse)
10842 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10845 if (ping_need_colortype_warning != MagickFalse ||
10846 ((mng_info->write_png_depth &&
10847 (int) mng_info->write_png_depth != ping_bit_depth) ||
10848 (mng_info->write_png_colortype &&
10849 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10850 mng_info->write_png_colortype != 7 &&
10851 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10853 if (logging != MagickFalse)
10855 if (ping_need_colortype_warning != MagickFalse)
10857 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10858 " Image has transparency but tRNS chunk was excluded");
10861 if (mng_info->write_png_depth)
10863 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10864 " Defined png:bit-depth=%u, Computed depth=%u",
10865 mng_info->write_png_depth,
10869 if (mng_info->write_png_colortype)
10871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872 " Defined png:color-type=%u, Computed color type=%u",
10873 mng_info->write_png_colortype-1,
10879 "Cannot write image with defined png:bit-depth or png:color-type.");
10882 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10884 /* Add an opaque matte channel */
10885 image->alpha_trait = BlendPixelTrait;
10886 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10888 if (logging != MagickFalse)
10889 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10890 " Added an opaque matte channel");
10893 if (number_transparent != 0 || number_semitransparent != 0)
10895 if (ping_color_type < 4)
10897 ping_have_tRNS=MagickTrue;
10898 if (logging != MagickFalse)
10899 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10900 " Setting ping_have_tRNS=MagickTrue.");
10904 if (logging != MagickFalse)
10905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10906 " Writing PNG header chunks");
10908 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10909 ping_bit_depth,ping_color_type,
10910 ping_interlace_method,ping_compression_method,
10911 ping_filter_method);
10913 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10915 png_set_PLTE(ping,ping_info,palette,number_colors);
10917 if (logging != MagickFalse)
10919 for (i=0; i< (ssize_t) number_colors; i++)
10921 if (i < ping_num_trans)
10922 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10923 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10925 (int) palette[i].red,
10926 (int) palette[i].green,
10927 (int) palette[i].blue,
10929 (int) ping_trans_alpha[i]);
10931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10932 " PLTE[%d] = (%d,%d,%d)",
10934 (int) palette[i].red,
10935 (int) palette[i].green,
10936 (int) palette[i].blue);
10941 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10942 if (ping_exclude_sRGB != MagickFalse ||
10943 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10945 if ((ping_exclude_tEXt == MagickFalse ||
10946 ping_exclude_zTXt == MagickFalse) &&
10947 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10949 ResetImageProfileIterator(image);
10950 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10952 profile=GetImageProfile(image,name);
10954 if (profile != (StringInfo *) NULL)
10956 #ifdef PNG_WRITE_iCCP_SUPPORTED
10957 if ((LocaleCompare(name,"ICC") == 0) ||
10958 (LocaleCompare(name,"ICM") == 0))
10960 ping_have_iCCP = MagickTrue;
10961 if (ping_exclude_iCCP == MagickFalse)
10963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10964 " Setting up iCCP chunk");
10966 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10967 #if (PNG_LIBPNG_VER < 10500)
10968 (png_charp) GetStringInfoDatum(profile),
10970 (const png_byte *) GetStringInfoDatum(profile),
10972 (png_uint_32) GetStringInfoLength(profile));
10976 /* Do not write hex-encoded ICC chunk */
10977 name=GetNextImageProfile(image);
10981 #endif /* WRITE_iCCP */
10983 #if defined(eXIf_SUPPORTED) || defined(exIf_SUPPORTED) || \
10984 defined(zxIf_SUPPORTED)
10985 if (LocaleCompare(name,"exif") == 0)
10987 /* Do not write hex-encoded ICC chunk; we will
10988 write it later as an eXIf or zxIf chunk */
10989 name=GetNextImageProfile(image);
10994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10995 " Setting up zTXt chunk with uuencoded %s profile",
10997 Magick_png_write_raw_profile(image_info,ping,ping_info,
10998 (unsigned char *) name,(unsigned char *) name,
10999 GetStringInfoDatum(profile),
11000 (png_uint_32) GetStringInfoLength(profile));
11002 name=GetNextImageProfile(image);
11007 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11008 if ((mng_info->have_write_global_srgb == 0) &&
11009 ping_have_iCCP != MagickTrue &&
11010 (ping_have_sRGB != MagickFalse ||
11011 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11013 if (ping_exclude_sRGB == MagickFalse)
11016 Note image rendering intent.
11018 if (logging != MagickFalse)
11019 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11020 " Setting up sRGB chunk");
11022 (void) png_set_sRGB(ping,ping_info,(
11023 Magick_RenderingIntent_to_PNG_RenderingIntent(
11024 image->rendering_intent)));
11026 ping_have_sRGB = MagickTrue;
11030 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11033 if (ping_exclude_gAMA == MagickFalse &&
11034 ping_have_iCCP == MagickFalse &&
11035 ping_have_sRGB == MagickFalse &&
11036 (ping_exclude_sRGB == MagickFalse ||
11037 (image->gamma < .45 || image->gamma > .46)))
11039 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11043 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11045 if (logging != MagickFalse)
11046 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11047 " Setting up gAMA chunk");
11049 png_set_gAMA(ping,ping_info,image->gamma);
11053 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11055 if ((mng_info->have_write_global_chrm == 0) &&
11056 (image->chromaticity.red_primary.x != 0.0))
11059 Note image chromaticity.
11060 Note: if cHRM+gAMA == sRGB write sRGB instead.
11068 wp=image->chromaticity.white_point;
11069 rp=image->chromaticity.red_primary;
11070 gp=image->chromaticity.green_primary;
11071 bp=image->chromaticity.blue_primary;
11073 if (logging != MagickFalse)
11074 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11075 " Setting up cHRM chunk");
11077 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11083 if (ping_exclude_bKGD == MagickFalse)
11085 if (ping_have_bKGD != MagickFalse)
11087 png_set_bKGD(ping,ping_info,&ping_background);
11088 if (logging != MagickFalse)
11090 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11091 " Setting up bKGD chunk");
11092 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11093 " background color = (%d,%d,%d)",
11094 (int) ping_background.red,
11095 (int) ping_background.green,
11096 (int) ping_background.blue);
11097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11098 " index = %d, gray=%d",
11099 (int) ping_background.index,
11100 (int) ping_background.gray);
11105 if (ping_exclude_pHYs == MagickFalse)
11107 if (ping_have_pHYs != MagickFalse)
11109 png_set_pHYs(ping,ping_info,
11110 ping_pHYs_x_resolution,
11111 ping_pHYs_y_resolution,
11112 ping_pHYs_unit_type);
11114 if (logging != MagickFalse)
11116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11117 " Setting up pHYs chunk");
11118 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11119 " x_resolution=%lu",
11120 (unsigned long) ping_pHYs_x_resolution);
11121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11122 " y_resolution=%lu",
11123 (unsigned long) ping_pHYs_y_resolution);
11124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11126 (unsigned long) ping_pHYs_unit_type);
11131 #if defined(PNG_tIME_SUPPORTED)
11132 if (ping_exclude_tIME == MagickFalse)
11137 if (image->taint == MagickFalse)
11139 timestamp=GetImageOption(image_info,"png:tIME");
11141 if (timestamp == (const char *) NULL)
11142 timestamp=GetImageProperty(image,"png:tIME",exception);
11147 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11148 " Reset tIME in tainted image");
11150 timestamp=GetImageProperty(image,"date:modify",exception);
11153 if (timestamp != (const char *) NULL)
11154 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11158 if (mng_info->need_blob != MagickFalse)
11160 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11162 png_error(ping,"WriteBlob Failed");
11164 ping_have_blob=MagickTrue;
11167 png_write_info_before_PLTE(ping, ping_info);
11169 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11171 if (logging != MagickFalse)
11173 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11174 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11177 if (ping_color_type == 3)
11178 (void) png_set_tRNS(ping, ping_info,
11185 (void) png_set_tRNS(ping, ping_info,
11188 &ping_trans_color);
11190 if (logging != MagickFalse)
11192 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11193 " tRNS color =(%d,%d,%d)",
11194 (int) ping_trans_color.red,
11195 (int) ping_trans_color.green,
11196 (int) ping_trans_color.blue);
11201 /* write any png-chunk-b profiles */
11202 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
11204 png_write_info(ping,ping_info);
11206 /* write any PNG-chunk-m profiles */
11207 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
11209 ping_wrote_caNv = MagickFalse;
11211 /* write caNv chunk */
11212 if (ping_exclude_caNv == MagickFalse)
11214 if ((image->page.width != 0 && image->page.width != image->columns) ||
11215 (image->page.height != 0 && image->page.height != image->rows) ||
11216 image->page.x != 0 || image->page.y != 0)
11221 (void) WriteBlobMSBULong(image,16L); /* data length=8 */
11222 PNGType(chunk,mng_caNv);
11223 LogPNGChunk(logging,mng_caNv,16L);
11224 PNGLong(chunk+4,(png_uint_32) image->page.width);
11225 PNGLong(chunk+8,(png_uint_32) image->page.height);
11226 PNGsLong(chunk+12,(png_int_32) image->page.x);
11227 PNGsLong(chunk+16,(png_int_32) image->page.y);
11228 (void) WriteBlob(image,20,chunk);
11229 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11230 ping_wrote_caNv = MagickTrue;
11234 #if defined(PNG_oFFs_SUPPORTED)
11235 if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11237 if (image->page.x || image->page.y)
11239 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11240 (png_int_32) image->page.y, 0);
11242 if (logging != MagickFalse)
11243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11244 " Setting up oFFs chunk with x=%d, y=%d, units=0",
11245 (int) image->page.x, (int) image->page.y);
11250 /* write vpAg chunk (deprecated, replaced by caNv) */
11251 if (ping_exclude_vpAg == MagickFalse && ping_wrote_caNv == MagickFalse)
11253 if ((image->page.width != 0 && image->page.width != image->columns) ||
11254 (image->page.height != 0 && image->page.height != image->rows))
11259 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11260 PNGType(chunk,mng_vpAg);
11261 LogPNGChunk(logging,mng_vpAg,9L);
11262 PNGLong(chunk+4,(png_uint_32) image->page.width);
11263 PNGLong(chunk+8,(png_uint_32) image->page.height);
11264 chunk[12]=0; /* unit = pixels */
11265 (void) WriteBlob(image,13,chunk);
11266 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11270 #if (PNG_LIBPNG_VER == 10206)
11271 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11272 #define PNG_HAVE_IDAT 0x04
11273 ping->mode |= PNG_HAVE_IDAT;
11274 #undef PNG_HAVE_IDAT
11277 png_set_packing(ping);
11281 rowbytes=image->columns;
11282 if (image_depth > 8)
11284 switch (ping_color_type)
11286 case PNG_COLOR_TYPE_RGB:
11290 case PNG_COLOR_TYPE_GRAY_ALPHA:
11294 case PNG_COLOR_TYPE_RGBA:
11302 if (logging != MagickFalse)
11304 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11305 " Writing PNG image data");
11307 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11308 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11310 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11311 if (pixel_info == (MemoryInfo *) NULL)
11312 png_error(ping,"Allocation of memory for pixels failed");
11313 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11316 Initialize image scanlines.
11318 quantum_info=AcquireQuantumInfo(image_info,image);
11319 if (quantum_info == (QuantumInfo *) NULL)
11320 png_error(ping,"Memory allocation for quantum_info failed");
11321 quantum_info->format=UndefinedQuantumFormat;
11322 SetQuantumDepth(image,quantum_info,image_depth);
11323 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11324 num_passes=png_set_interlace_handling(ping);
11326 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11327 !mng_info->write_png48 && !mng_info->write_png64 &&
11328 !mng_info->write_png32) &&
11329 (mng_info->IsPalette ||
11330 (image_info->type == BilevelType)) &&
11331 image_matte == MagickFalse &&
11332 ping_have_non_bw == MagickFalse)
11334 /* Palette, Bilevel, or Opaque Monochrome */
11335 register const Quantum
11338 SetQuantumDepth(image,quantum_info,8);
11339 for (pass=0; pass < num_passes; pass++)
11342 Convert PseudoClass image to a PNG monochrome image.
11344 for (y=0; y < (ssize_t) image->rows; y++)
11346 if (logging != MagickFalse && y == 0)
11347 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11348 " Writing row of pixels (0)");
11350 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11352 if (p == (const Quantum *) NULL)
11355 if (mng_info->IsPalette)
11357 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11358 quantum_info,GrayQuantum,ping_pixels,exception);
11359 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11360 mng_info->write_png_depth &&
11361 mng_info->write_png_depth != old_bit_depth)
11363 /* Undo pixel scaling */
11364 for (i=0; i < (ssize_t) image->columns; i++)
11365 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11366 >> (8-old_bit_depth));
11372 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11373 quantum_info,RedQuantum,ping_pixels,exception);
11376 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11377 for (i=0; i < (ssize_t) image->columns; i++)
11378 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11381 if (logging != MagickFalse && y == 0)
11382 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11383 " Writing row of pixels (1)");
11385 png_write_row(ping,ping_pixels);
11387 status=SetImageProgress(image,SaveImageTag,
11388 (MagickOffsetType) (pass * image->rows + y),
11389 num_passes * image->rows);
11391 if (status == MagickFalse)
11397 else /* Not Palette, Bilevel, or Opaque Monochrome */
11399 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11400 !mng_info->write_png48 && !mng_info->write_png64 &&
11401 !mng_info->write_png32) && (image_matte != MagickFalse ||
11402 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11403 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11405 register const Quantum
11408 for (pass=0; pass < num_passes; pass++)
11411 for (y=0; y < (ssize_t) image->rows; y++)
11413 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11415 if (p == (const Quantum *) NULL)
11418 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11420 if (mng_info->IsPalette)
11421 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11422 quantum_info,GrayQuantum,ping_pixels,exception);
11425 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11426 quantum_info,RedQuantum,ping_pixels,exception);
11428 if (logging != MagickFalse && y == 0)
11429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430 " Writing GRAY PNG pixels (2)");
11433 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11435 if (logging != MagickFalse && y == 0)
11436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437 " Writing GRAY_ALPHA PNG pixels (2)");
11439 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11440 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11443 if (logging != MagickFalse && y == 0)
11444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11445 " Writing row of pixels (2)");
11447 png_write_row(ping,ping_pixels);
11449 status=SetImageProgress(image,SaveImageTag,
11450 (MagickOffsetType) (pass * image->rows + y),
11451 num_passes * image->rows);
11453 if (status == MagickFalse)
11461 register const Quantum
11464 for (pass=0; pass < num_passes; pass++)
11466 if ((image_depth > 8) ||
11467 mng_info->write_png24 ||
11468 mng_info->write_png32 ||
11469 mng_info->write_png48 ||
11470 mng_info->write_png64 ||
11471 (!mng_info->write_png8 && !mng_info->IsPalette))
11473 for (y=0; y < (ssize_t) image->rows; y++)
11475 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11477 if (p == (const Quantum *) NULL)
11480 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11482 if (image->storage_class == DirectClass)
11483 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11484 quantum_info,RedQuantum,ping_pixels,exception);
11487 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11488 quantum_info,GrayQuantum,ping_pixels,exception);
11491 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11493 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11494 quantum_info,GrayAlphaQuantum,ping_pixels,
11497 if (logging != MagickFalse && y == 0)
11498 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11499 " Writing GRAY_ALPHA PNG pixels (3)");
11502 else if (image_matte != MagickFalse)
11503 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11504 quantum_info,RGBAQuantum,ping_pixels,exception);
11507 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11508 quantum_info,RGBQuantum,ping_pixels,exception);
11510 if (logging != MagickFalse && y == 0)
11511 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11512 " Writing row of pixels (3)");
11514 png_write_row(ping,ping_pixels);
11516 status=SetImageProgress(image,SaveImageTag,
11517 (MagickOffsetType) (pass * image->rows + y),
11518 num_passes * image->rows);
11520 if (status == MagickFalse)
11526 /* not ((image_depth > 8) ||
11527 mng_info->write_png24 || mng_info->write_png32 ||
11528 mng_info->write_png48 || mng_info->write_png64 ||
11529 (!mng_info->write_png8 && !mng_info->IsPalette))
11532 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11533 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11535 if (logging != MagickFalse)
11536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11537 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11539 SetQuantumDepth(image,quantum_info,8);
11543 for (y=0; y < (ssize_t) image->rows; y++)
11545 if (logging != MagickFalse && y == 0)
11546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11547 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11550 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11552 if (p == (const Quantum *) NULL)
11555 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11557 SetQuantumDepth(image,quantum_info,image->depth);
11559 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11560 quantum_info,GrayQuantum,ping_pixels,exception);
11563 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11565 if (logging != MagickFalse && y == 0)
11566 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567 " Writing GRAY_ALPHA PNG pixels (4)");
11569 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11570 quantum_info,GrayAlphaQuantum,ping_pixels,
11576 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11577 quantum_info,IndexQuantum,ping_pixels,exception);
11579 if (logging != MagickFalse && y <= 2)
11581 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582 " Writing row of non-gray pixels (4)");
11584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11586 (int)ping_pixels[0],(int)ping_pixels[1]);
11589 png_write_row(ping,ping_pixels);
11591 status=SetImageProgress(image,SaveImageTag,
11592 (MagickOffsetType) (pass * image->rows + y),
11593 num_passes * image->rows);
11595 if (status == MagickFalse)
11603 if (quantum_info != (QuantumInfo *) NULL)
11604 quantum_info=DestroyQuantumInfo(quantum_info);
11606 if (logging != MagickFalse)
11608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11609 " Wrote PNG image data");
11611 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11612 " Width: %.20g",(double) ping_width);
11614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11615 " Height: %.20g",(double) ping_height);
11617 if (mng_info->write_png_depth)
11619 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11620 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11623 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11624 " PNG bit-depth written: %d",ping_bit_depth);
11626 if (mng_info->write_png_colortype)
11628 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11629 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11633 " PNG color-type written: %d",ping_color_type);
11635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11636 " PNG Interlace method: %d",ping_interlace_method);
11639 Generate text chunks after IDAT.
11641 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11643 ResetImagePropertyIterator(image);
11644 property=GetNextImageProperty(image);
11645 while (property != (const char *) NULL)
11650 value=GetImageProperty(image,property,exception);
11652 /* Don't write any "png:" or "jpeg:" properties; those are just for
11653 * "identify" or for passing through to another JPEG
11655 if ((LocaleNCompare(property,"png:",4) != 0 &&
11656 LocaleNCompare(property,"jpeg:",5) != 0) &&
11659 /* Suppress density and units if we wrote a pHYs chunk */
11660 (ping_exclude_pHYs != MagickFalse ||
11661 LocaleCompare(property,"density") != 0 ||
11662 LocaleCompare(property,"units") != 0) &&
11664 /* Suppress the IM-generated Date:create and Date:modify */
11665 (ping_exclude_date == MagickFalse ||
11666 LocaleNCompare(property, "Date:",5) != 0))
11668 if (value != (const char *) NULL)
11671 #if PNG_LIBPNG_VER >= 10400
11672 text=(png_textp) png_malloc(ping,
11673 (png_alloc_size_t) sizeof(png_text));
11675 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11677 text[0].key=(char *) property;
11678 text[0].text=(char *) value;
11679 text[0].text_length=strlen(value);
11681 if (ping_exclude_tEXt != MagickFalse)
11682 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11684 else if (ping_exclude_zTXt != MagickFalse)
11685 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11689 text[0].compression=image_info->compression == NoCompression ||
11690 (image_info->compression == UndefinedCompression &&
11691 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11692 PNG_TEXT_COMPRESSION_zTXt ;
11695 if (logging != MagickFalse)
11697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11698 " Setting up text chunk");
11700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11701 " keyword: '%s'",text[0].key);
11704 png_set_text(ping,ping_info,text,1);
11705 png_free(ping,text);
11708 property=GetNextImageProperty(image);
11712 /* write any PNG-chunk-e profiles */
11713 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11715 #if defined(exIf_SUPPORTED) || defined(zxIf_SUPPORTED)
11716 /* write exIf profile */
11718 if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11722 ImageProfileIterator
11725 profile_iterator=AllocateImageProfileIterator(image);
11726 if (profile_iterator)
11731 const unsigned char
11737 while (NextImageProfile(profile_iterator,&profile_name,&profile_info,
11738 &profile_length) != MagickFail)
11740 if (LocaleCompare(profile_name,"exif") == 0)
11746 ResetImageProfileIterator(image);
11748 for (name=GetNextImageProfile(image);
11749 name != (const char *) NULL; )
11751 if (LocaleCompare(name,"exif") == 0)
11756 profile=GetImageProfile(image,name);
11758 if (profile != (StringInfo *) NULL)
11771 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11772 " Have eXIf profile");
11776 length=(png_uint_32) profile_length;
11778 ping_profile=CloneStringInfo(profile);
11779 data=GetStringInfoDatum(ping_profile),
11780 length=(png_uint_32) GetStringInfoLength(ping_profile);
11783 #if defined(zxIf_write_SUPPORTED)
11784 /* For now, write uncompressed exIf/eXIf chunk */
11787 #if 0 /* eXIf chunk is registered */
11788 PNGType(chunk,mng_eXIf);
11789 #else /* eXIf chunk not yet registered; write exIf instead */
11790 PNGType(chunk,mng_exIf);
11793 break; /* othewise crashes */
11795 /* skip the "Exif\0\0" JFIF Exif Header ID */
11798 LogPNGChunk(logging,chunk,length);
11799 (void) WriteBlobMSBULong(image,length);
11800 (void) WriteBlob(image,4,chunk);
11801 (void) WriteBlob(image,length,data+6);
11802 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11803 data+6, (uInt) length));
11806 name=GetNextImageProfile(image);
11812 #endif /* exIf_SUPPORTED) || zxIf_SUPPORTED */
11814 if (logging != MagickFalse)
11815 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11816 " Writing PNG end info");
11818 png_write_end(ping,ping_info);
11820 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11822 if (mng_info->page.x || mng_info->page.y ||
11823 (ping_width != mng_info->page.width) ||
11824 (ping_height != mng_info->page.height))
11830 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11832 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11833 PNGType(chunk,mng_FRAM);
11834 LogPNGChunk(logging,mng_FRAM,27L);
11836 chunk[5]=0; /* frame name separator (no name) */
11837 chunk[6]=1; /* flag for changing delay, for next frame only */
11838 chunk[7]=0; /* flag for changing frame timeout */
11839 chunk[8]=1; /* flag for changing frame clipping for next frame */
11840 chunk[9]=0; /* flag for changing frame sync_id */
11841 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11842 chunk[14]=0; /* clipping boundaries delta type */
11843 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11845 (png_uint_32) (mng_info->page.x + ping_width));
11846 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11848 (png_uint_32) (mng_info->page.y + ping_height));
11849 (void) WriteBlob(image,31,chunk);
11850 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11851 mng_info->old_framing_mode=4;
11852 mng_info->framing_mode=1;
11856 mng_info->framing_mode=3;
11858 if (mng_info->write_mng && !mng_info->need_fram &&
11859 ((int) image->dispose == 3))
11860 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11863 Free PNG resources.
11866 png_destroy_write_struct(&ping,&ping_info);
11868 pixel_info=RelinquishVirtualMemory(pixel_info);
11870 if (ping_have_blob != MagickFalse)
11871 (void) CloseBlob(image);
11873 image_info=DestroyImageInfo(image_info);
11874 image=DestroyImage(image);
11876 /* Store bit depth actually written */
11877 s[0]=(char) ping_bit_depth;
11880 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11882 if (logging != MagickFalse)
11883 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11884 " exit WriteOnePNGImage()");
11886 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11887 UnlockSemaphoreInfo(ping_semaphore);
11890 /* } for navigation to beginning of SETJMP-protected block. Revert to
11891 * Throwing an Exception when an error occurs.
11894 return(MagickTrue);
11895 /* End write one PNG image */
11900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11904 % W r i t e P N G I m a g e %
11908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11910 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11911 % Multiple-image Network Graphics (MNG) image file.
11913 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11915 % The format of the WritePNGImage method is:
11917 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11918 % Image *image,ExceptionInfo *exception)
11920 % A description of each parameter follows:
11922 % o image_info: the image info.
11924 % o image: The image.
11926 % o exception: return any errors or warnings in this structure.
11928 % Returns MagickTrue on success, MagickFalse on failure.
11930 % Communicating with the PNG encoder:
11932 % While the datastream written is always in PNG format and normally would
11933 % be given the "png" file extension, this method also writes the following
11934 % pseudo-formats which are subsets of png:
11936 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11937 % a depth greater than 8, the depth is reduced. If transparency
11938 % is present, the tRNS chunk must only have values 0 and 255
11939 % (i.e., transparency is binary: fully opaque or fully
11940 % transparent). If other values are present they will be
11941 % 50%-thresholded to binary transparency. If more than 256
11942 % colors are present, they will be quantized to the 4-4-4-1,
11943 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11944 % of any resulting fully-transparent pixels is changed to
11945 % the image's background color.
11947 % If you want better quantization or dithering of the colors
11948 % or alpha than that, you need to do it before calling the
11949 % PNG encoder. The pixels contain 8-bit indices even if
11950 % they could be represented with 1, 2, or 4 bits. Grayscale
11951 % images will be written as indexed PNG files even though the
11952 % PNG grayscale type might be slightly more efficient. Please
11953 % note that writing to the PNG8 format may result in loss
11954 % of color and alpha data.
11956 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11957 % chunk can be present to convey binary transparency by naming
11958 % one of the colors as transparent. The only loss incurred
11959 % is reduction of sample depth to 8. If the image has more
11960 % than one transparent color, has semitransparent pixels, or
11961 % has an opaque pixel with the same RGB components as the
11962 % transparent color, an image is not written.
11964 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11965 % transparency is permitted, i.e., the alpha sample for
11966 % each pixel can have any value from 0 to 255. The alpha
11967 % channel is present even if the image is fully opaque.
11968 % The only loss in data is the reduction of the sample depth
11971 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11972 % chunk can be present to convey binary transparency by naming
11973 % one of the colors as transparent. If the image has more
11974 % than one transparent color, has semitransparent pixels, or
11975 % has an opaque pixel with the same RGB components as the
11976 % transparent color, an image is not written.
11978 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11979 % transparency is permitted, i.e., the alpha sample for
11980 % each pixel can have any value from 0 to 65535. The alpha
11981 % channel is present even if the image is fully opaque.
11983 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11984 % image, if the input was a PNG, is written. If these values
11985 % cannot be found, or if the pixels have been changed in a way
11986 % that makes this impossible, then "PNG00" falls back to the
11987 % regular "PNG" format.
11989 % o -define: For more precise control of the PNG output, you can use the
11990 % Image options "png:bit-depth" and "png:color-type". These
11991 % can be set from the commandline with "-define" and also
11992 % from the application programming interfaces. The options
11993 % are case-independent and are converted to lowercase before
11994 % being passed to this encoder.
11996 % png:color-type can be 0, 2, 3, 4, or 6.
11998 % When png:color-type is 0 (Grayscale), png:bit-depth can
11999 % be 1, 2, 4, 8, or 16.
12001 % When png:color-type is 2 (RGB), png:bit-depth can
12004 % When png:color-type is 3 (Indexed), png:bit-depth can
12005 % be 1, 2, 4, or 8. This refers to the number of bits
12006 % used to store the index. The color samples always have
12007 % bit-depth 8 in indexed PNG files.
12009 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12010 % png:bit-depth can be 8 or 16.
12012 % If the image cannot be written without loss with the
12013 % requested bit-depth and color-type, a PNG file will not
12014 % be written, a warning will be issued, and the encoder will
12015 % return MagickFalse.
12017 % Since image encoders should not be responsible for the "heavy lifting",
12018 % the user should make sure that ImageMagick has already reduced the
12019 % image depth and number of colors and limit transparency to binary
12020 % transparency prior to attempting to write the image with depth, color,
12021 % or transparency limitations.
12023 % Note that another definition, "png:bit-depth-written" exists, but it
12024 % is not intended for external use. It is only used internally by the
12025 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12027 % It is possible to request that the PNG encoder write previously-formatted
12028 % ancillary chunks in the output PNG file, using the "-profile" commandline
12029 % option as shown below or by setting the profile via a programming
12032 % -profile PNG-chunk-x:<file>
12034 % where x is a location flag and <file> is a file containing the chunk
12035 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
12036 % This encoder will compute the chunk length and CRC, so those must not
12037 % be included in the file.
12039 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
12040 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
12041 % of the same type, then add a short unique string after the "x" to prevent
12042 % subsequent profiles from overwriting the preceding ones, e.g.,
12044 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
12046 % As of version 6.6.6 the following optimizations are always done:
12048 % o 32-bit depth is reduced to 16.
12049 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
12050 % high byte and low byte are identical.
12051 % o Palette is sorted to remove unused entries and to put a
12052 % transparent color first, if BUILD_PNG_PALETTE is defined.
12053 % o Opaque matte channel is removed when writing an indexed PNG.
12054 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
12055 % this can be done without loss and a larger bit depth N was not
12056 % requested via the "-define png:bit-depth=N" option.
12057 % o If matte channel is present but only one transparent color is
12058 % present, RGB+tRNS is written instead of RGBA
12059 % o Opaque matte channel is removed (or added, if color-type 4 or 6
12060 % was requested when converting an opaque image).
12062 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12064 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12065 Image *image,ExceptionInfo *exception)
12070 have_mng_structure,
12085 assert(image_info != (const ImageInfo *) NULL);
12086 assert(image_info->signature == MagickCoreSignature);
12087 assert(image != (Image *) NULL);
12088 assert(image->signature == MagickCoreSignature);
12089 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12090 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12092 Allocate a MngInfo structure.
12094 have_mng_structure=MagickFalse;
12095 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12097 if (mng_info == (MngInfo *) NULL)
12098 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12101 Initialize members of the MngInfo structure.
12103 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12104 mng_info->image=image;
12105 mng_info->equal_backgrounds=MagickTrue;
12106 have_mng_structure=MagickTrue;
12108 /* See if user has requested a specific PNG subformat */
12110 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12111 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12112 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12113 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12114 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12116 value=GetImageOption(image_info,"png:format");
12118 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12120 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12121 " Format=%s",value);
12123 mng_info->write_png8 = MagickFalse;
12124 mng_info->write_png24 = MagickFalse;
12125 mng_info->write_png32 = MagickFalse;
12126 mng_info->write_png48 = MagickFalse;
12127 mng_info->write_png64 = MagickFalse;
12129 if (LocaleCompare(value,"png8") == 0)
12130 mng_info->write_png8 = MagickTrue;
12132 else if (LocaleCompare(value,"png24") == 0)
12133 mng_info->write_png24 = MagickTrue;
12135 else if (LocaleCompare(value,"png32") == 0)
12136 mng_info->write_png32 = MagickTrue;
12138 else if (LocaleCompare(value,"png48") == 0)
12139 mng_info->write_png48 = MagickTrue;
12141 else if (LocaleCompare(value,"png64") == 0)
12142 mng_info->write_png64 = MagickTrue;
12144 else if ((LocaleCompare(value,"png00") == 0) ||
12145 LocaleCompare(image_info->magick,"PNG00") == 0)
12147 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12148 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12150 if (value != (char *) NULL)
12152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12153 " png00 inherited bit depth=%s",value);
12155 if (LocaleCompare(value,"1") == 0)
12156 mng_info->write_png_depth = 1;
12158 else if (LocaleCompare(value,"2") == 0)
12159 mng_info->write_png_depth = 2;
12161 else if (LocaleCompare(value,"4") == 0)
12162 mng_info->write_png_depth = 4;
12164 else if (LocaleCompare(value,"8") == 0)
12165 mng_info->write_png_depth = 8;
12167 else if (LocaleCompare(value,"16") == 0)
12168 mng_info->write_png_depth = 16;
12171 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12173 if (value != (char *) NULL)
12175 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12176 " png00 inherited color type=%s",value);
12178 if (LocaleCompare(value,"0") == 0)
12179 mng_info->write_png_colortype = 1;
12181 else if (LocaleCompare(value,"2") == 0)
12182 mng_info->write_png_colortype = 3;
12184 else if (LocaleCompare(value,"3") == 0)
12185 mng_info->write_png_colortype = 4;
12187 else if (LocaleCompare(value,"4") == 0)
12188 mng_info->write_png_colortype = 5;
12190 else if (LocaleCompare(value,"6") == 0)
12191 mng_info->write_png_colortype = 7;
12196 if (mng_info->write_png8)
12198 mng_info->write_png_colortype = /* 3 */ 4;
12199 mng_info->write_png_depth = 8;
12203 if (mng_info->write_png24)
12205 mng_info->write_png_colortype = /* 2 */ 3;
12206 mng_info->write_png_depth = 8;
12209 if (image->alpha_trait != UndefinedPixelTrait)
12210 (void) SetImageType(image,TrueColorAlphaType,exception);
12213 (void) SetImageType(image,TrueColorType,exception);
12215 (void) SyncImage(image,exception);
12218 if (mng_info->write_png32)
12220 mng_info->write_png_colortype = /* 6 */ 7;
12221 mng_info->write_png_depth = 8;
12223 image->alpha_trait = BlendPixelTrait;
12225 (void) SetImageType(image,TrueColorAlphaType,exception);
12226 (void) SyncImage(image,exception);
12229 if (mng_info->write_png48)
12231 mng_info->write_png_colortype = /* 2 */ 3;
12232 mng_info->write_png_depth = 16;
12235 if (image->alpha_trait != UndefinedPixelTrait)
12236 (void) SetImageType(image,TrueColorAlphaType,exception);
12239 (void) SetImageType(image,TrueColorType,exception);
12241 (void) SyncImage(image,exception);
12244 if (mng_info->write_png64)
12246 mng_info->write_png_colortype = /* 6 */ 7;
12247 mng_info->write_png_depth = 16;
12249 image->alpha_trait = BlendPixelTrait;
12251 (void) SetImageType(image,TrueColorAlphaType,exception);
12252 (void) SyncImage(image,exception);
12255 value=GetImageOption(image_info,"png:bit-depth");
12257 if (value != (char *) NULL)
12259 if (LocaleCompare(value,"1") == 0)
12260 mng_info->write_png_depth = 1;
12262 else if (LocaleCompare(value,"2") == 0)
12263 mng_info->write_png_depth = 2;
12265 else if (LocaleCompare(value,"4") == 0)
12266 mng_info->write_png_depth = 4;
12268 else if (LocaleCompare(value,"8") == 0)
12269 mng_info->write_png_depth = 8;
12271 else if (LocaleCompare(value,"16") == 0)
12272 mng_info->write_png_depth = 16;
12275 (void) ThrowMagickException(exception,
12276 GetMagickModule(),CoderWarning,
12277 "ignoring invalid defined png:bit-depth",
12280 if (logging != MagickFalse)
12281 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12282 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12285 value=GetImageOption(image_info,"png:color-type");
12287 if (value != (char *) NULL)
12289 /* We must store colortype+1 because 0 is a valid colortype */
12290 if (LocaleCompare(value,"0") == 0)
12291 mng_info->write_png_colortype = 1;
12293 else if (LocaleCompare(value,"1") == 0)
12294 mng_info->write_png_colortype = 2;
12296 else if (LocaleCompare(value,"2") == 0)
12297 mng_info->write_png_colortype = 3;
12299 else if (LocaleCompare(value,"3") == 0)
12300 mng_info->write_png_colortype = 4;
12302 else if (LocaleCompare(value,"4") == 0)
12303 mng_info->write_png_colortype = 5;
12305 else if (LocaleCompare(value,"6") == 0)
12306 mng_info->write_png_colortype = 7;
12309 (void) ThrowMagickException(exception,
12310 GetMagickModule(),CoderWarning,
12311 "ignoring invalid defined png:color-type",
12314 if (logging != MagickFalse)
12315 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12316 " png:color-type=%d was defined.\n",
12317 mng_info->write_png_colortype-1);
12320 /* Check for chunks to be excluded:
12322 * The default is to not exclude any known chunks except for any
12323 * listed in the "unused_chunks" array, above.
12325 * Chunks can be listed for exclusion via a "png:exclude-chunk"
12326 * define (in the image properties or in the image artifacts)
12327 * or via a mng_info member. For convenience, in addition
12328 * to or instead of a comma-separated list of chunks, the
12329 * "exclude-chunk" string can be simply "all" or "none".
12331 * The exclude-chunk define takes priority over the mng_info.
12333 * A "png:include-chunk" define takes priority over both the
12334 * mng_info and the "png:exclude-chunk" define. Like the
12335 * "exclude-chunk" string, it can define "all" or "none" as
12336 * well as a comma-separated list. Chunks that are unknown to
12337 * ImageMagick are always excluded, regardless of their "copy-safe"
12338 * status according to the PNG specification, and even if they
12339 * appear in the "include-chunk" list. Such defines appearing among
12340 * the image options take priority over those found among the image
12343 * Finally, all chunks listed in the "unused_chunks" array are
12344 * automatically excluded, regardless of the other instructions
12347 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12348 * will not be written and the gAMA chunk will only be written if it
12349 * is not between .45 and .46, or approximately (1.0/2.2).
12351 * If you exclude tRNS and the image has transparency, the colortype
12352 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12354 * The -strip option causes StripImage() to set the png:include-chunk
12355 * artifact to "none,trns,gama".
12358 mng_info->ping_exclude_bKGD=MagickFalse;
12359 mng_info->ping_exclude_caNv=MagickFalse;
12360 mng_info->ping_exclude_cHRM=MagickFalse;
12361 mng_info->ping_exclude_date=MagickFalse;
12362 mng_info->ping_exclude_eXIf=MagickFalse;
12363 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12364 mng_info->ping_exclude_gAMA=MagickFalse;
12365 mng_info->ping_exclude_iCCP=MagickFalse;
12366 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12367 mng_info->ping_exclude_oFFs=MagickFalse;
12368 mng_info->ping_exclude_pHYs=MagickFalse;
12369 mng_info->ping_exclude_sRGB=MagickFalse;
12370 mng_info->ping_exclude_tEXt=MagickFalse;
12371 mng_info->ping_exclude_tIME=MagickFalse;
12372 mng_info->ping_exclude_tRNS=MagickFalse;
12373 mng_info->ping_exclude_vpAg=MagickFalse;
12374 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12375 mng_info->ping_exclude_zTXt=MagickFalse;
12377 mng_info->ping_preserve_colormap=MagickFalse;
12379 value=GetImageOption(image_info,"png:preserve-colormap");
12381 value=GetImageArtifact(image,"png:preserve-colormap");
12383 mng_info->ping_preserve_colormap=MagickTrue;
12385 mng_info->ping_preserve_iCCP=MagickFalse;
12387 value=GetImageOption(image_info,"png:preserve-iCCP");
12389 value=GetImageArtifact(image,"png:preserve-iCCP");
12391 mng_info->ping_preserve_iCCP=MagickTrue;
12393 /* These compression-level, compression-strategy, and compression-filter
12394 * defines take precedence over values from the -quality option.
12396 value=GetImageOption(image_info,"png:compression-level");
12398 value=GetImageArtifact(image,"png:compression-level");
12401 /* We have to add 1 to everything because 0 is a valid input,
12402 * and we want to use 0 (the default) to mean undefined.
12404 if (LocaleCompare(value,"0") == 0)
12405 mng_info->write_png_compression_level = 1;
12407 else if (LocaleCompare(value,"1") == 0)
12408 mng_info->write_png_compression_level = 2;
12410 else if (LocaleCompare(value,"2") == 0)
12411 mng_info->write_png_compression_level = 3;
12413 else if (LocaleCompare(value,"3") == 0)
12414 mng_info->write_png_compression_level = 4;
12416 else if (LocaleCompare(value,"4") == 0)
12417 mng_info->write_png_compression_level = 5;
12419 else if (LocaleCompare(value,"5") == 0)
12420 mng_info->write_png_compression_level = 6;
12422 else if (LocaleCompare(value,"6") == 0)
12423 mng_info->write_png_compression_level = 7;
12425 else if (LocaleCompare(value,"7") == 0)
12426 mng_info->write_png_compression_level = 8;
12428 else if (LocaleCompare(value,"8") == 0)
12429 mng_info->write_png_compression_level = 9;
12431 else if (LocaleCompare(value,"9") == 0)
12432 mng_info->write_png_compression_level = 10;
12435 (void) ThrowMagickException(exception,
12436 GetMagickModule(),CoderWarning,
12437 "ignoring invalid defined png:compression-level",
12441 value=GetImageOption(image_info,"png:compression-strategy");
12443 value=GetImageArtifact(image,"png:compression-strategy");
12446 if (LocaleCompare(value,"0") == 0)
12447 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12449 else if (LocaleCompare(value,"1") == 0)
12450 mng_info->write_png_compression_strategy = Z_FILTERED+1;
12452 else if (LocaleCompare(value,"2") == 0)
12453 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12455 else if (LocaleCompare(value,"3") == 0)
12456 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
12457 mng_info->write_png_compression_strategy = Z_RLE+1;
12459 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12462 else if (LocaleCompare(value,"4") == 0)
12463 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
12464 mng_info->write_png_compression_strategy = Z_FIXED+1;
12466 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12470 (void) ThrowMagickException(exception,
12471 GetMagickModule(),CoderWarning,
12472 "ignoring invalid defined png:compression-strategy",
12476 value=GetImageOption(image_info,"png:compression-filter");
12478 value=GetImageArtifact(image,"png:compression-filter");
12481 /* To do: combinations of filters allowed by libpng
12482 * masks 0x08 through 0xf8
12484 * Implement this as a comma-separated list of 0,1,2,3,4,5
12485 * where 5 is a special case meaning PNG_ALL_FILTERS.
12488 if (LocaleCompare(value,"0") == 0)
12489 mng_info->write_png_compression_filter = 1;
12491 else if (LocaleCompare(value,"1") == 0)
12492 mng_info->write_png_compression_filter = 2;
12494 else if (LocaleCompare(value,"2") == 0)
12495 mng_info->write_png_compression_filter = 3;
12497 else if (LocaleCompare(value,"3") == 0)
12498 mng_info->write_png_compression_filter = 4;
12500 else if (LocaleCompare(value,"4") == 0)
12501 mng_info->write_png_compression_filter = 5;
12503 else if (LocaleCompare(value,"5") == 0)
12504 mng_info->write_png_compression_filter = 6;
12507 (void) ThrowMagickException(exception,
12508 GetMagickModule(),CoderWarning,
12509 "ignoring invalid defined png:compression-filter",
12513 for (source=0; source<8; source++)
12518 value=GetImageOption(image_info,"png:exclude-chunks");
12521 value=GetImageArtifact(image,"png:exclude-chunks");
12524 value=GetImageOption(image_info,"png:exclude-chunk");
12527 value=GetImageArtifact(image,"png:exclude-chunk");
12530 value=GetImageOption(image_info,"png:include-chunks");
12533 value=GetImageArtifact(image,"png:include-chunks");
12536 value=GetImageOption(image_info,"png:include-chunk");
12539 value=GetImageArtifact(image,"png:include-chunk");
12545 excluding = MagickTrue;
12547 excluding = MagickFalse;
12549 if (logging != MagickFalse)
12551 if (source == 0 || source == 2)
12552 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12553 " png:exclude-chunk=%s found in image options.\n", value);
12554 else if (source == 1 || source == 3)
12555 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12556 " png:exclude-chunk=%s found in image artifacts.\n", value);
12557 else if (source == 4 || source == 6)
12558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12559 " png:include-chunk=%s found in image options.\n", value);
12560 else /* if (source == 5 || source == 7) */
12561 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562 " png:include-chunk=%s found in image artifacts.\n", value);
12565 if (IsOptionMember("all",value) != MagickFalse)
12567 mng_info->ping_exclude_bKGD=excluding;
12568 mng_info->ping_exclude_caNv=excluding;
12569 mng_info->ping_exclude_cHRM=excluding;
12570 mng_info->ping_exclude_date=excluding;
12571 mng_info->ping_exclude_EXIF=excluding;
12572 mng_info->ping_exclude_eXIf=excluding;
12573 mng_info->ping_exclude_gAMA=excluding;
12574 mng_info->ping_exclude_iCCP=excluding;
12575 /* mng_info->ping_exclude_iTXt=excluding; */
12576 mng_info->ping_exclude_oFFs=excluding;
12577 mng_info->ping_exclude_pHYs=excluding;
12578 mng_info->ping_exclude_sRGB=excluding;
12579 mng_info->ping_exclude_tEXt=excluding;
12580 mng_info->ping_exclude_tIME=excluding;
12581 mng_info->ping_exclude_tRNS=excluding;
12582 mng_info->ping_exclude_vpAg=excluding;
12583 mng_info->ping_exclude_zCCP=excluding;
12584 mng_info->ping_exclude_zTXt=excluding;
12587 if (IsOptionMember("none",value) != MagickFalse)
12589 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12591 mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12593 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12595 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12597 mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12599 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12601 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12603 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12605 /* mng_info->ping_exclude_iTXt=!excluding; */
12606 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12608 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12610 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12612 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12614 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12616 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12618 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12620 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12622 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12626 if (IsOptionMember("bkgd",value) != MagickFalse)
12627 mng_info->ping_exclude_bKGD=excluding;
12629 if (IsOptionMember("caNv",value) != MagickFalse)
12630 mng_info->ping_exclude_caNv=excluding;
12632 if (IsOptionMember("chrm",value) != MagickFalse)
12633 mng_info->ping_exclude_cHRM=excluding;
12635 if (IsOptionMember("date",value) != MagickFalse)
12636 mng_info->ping_exclude_date=excluding;
12638 if (IsOptionMember("exif",value) != MagickFalse)
12640 mng_info->ping_exclude_EXIF=excluding;
12641 mng_info->ping_exclude_eXIf=excluding;
12644 if (IsOptionMember("gama",value) != MagickFalse)
12645 mng_info->ping_exclude_gAMA=excluding;
12647 if (IsOptionMember("iccp",value) != MagickFalse)
12648 mng_info->ping_exclude_iCCP=excluding;
12651 if (IsOptionMember("itxt",value) != MagickFalse)
12652 mng_info->ping_exclude_iTXt=excluding;
12655 if (IsOptionMember("offs",value) != MagickFalse)
12656 mng_info->ping_exclude_oFFs=excluding;
12658 if (IsOptionMember("phys",value) != MagickFalse)
12659 mng_info->ping_exclude_pHYs=excluding;
12661 if (IsOptionMember("srgb",value) != MagickFalse)
12662 mng_info->ping_exclude_sRGB=excluding;
12664 if (IsOptionMember("text",value) != MagickFalse)
12665 mng_info->ping_exclude_tEXt=excluding;
12667 if (IsOptionMember("time",value) != MagickFalse)
12668 mng_info->ping_exclude_tIME=excluding;
12670 if (IsOptionMember("trns",value) != MagickFalse)
12671 mng_info->ping_exclude_tRNS=excluding;
12673 if (IsOptionMember("vpag",value) != MagickFalse)
12674 mng_info->ping_exclude_vpAg=excluding;
12676 if (IsOptionMember("zccp",value) != MagickFalse)
12677 mng_info->ping_exclude_zCCP=excluding;
12679 if (IsOptionMember("ztxt",value) != MagickFalse)
12680 mng_info->ping_exclude_zTXt=excluding;
12683 if (logging != MagickFalse)
12685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686 " Chunks to be excluded from the output png:");
12687 if (mng_info->ping_exclude_bKGD != MagickFalse)
12688 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12690 if (mng_info->ping_exclude_caNv != MagickFalse)
12691 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12693 if (mng_info->ping_exclude_cHRM != MagickFalse)
12694 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12696 if (mng_info->ping_exclude_date != MagickFalse)
12697 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12699 if (mng_info->ping_exclude_EXIF != MagickFalse)
12700 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12702 if (mng_info->ping_exclude_eXIf != MagickFalse)
12703 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12705 if (mng_info->ping_exclude_gAMA != MagickFalse)
12706 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12708 if (mng_info->ping_exclude_iCCP != MagickFalse)
12709 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12712 if (mng_info->ping_exclude_iTXt != MagickFalse)
12713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12717 if (mng_info->ping_exclude_oFFs != MagickFalse)
12718 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12720 if (mng_info->ping_exclude_pHYs != MagickFalse)
12721 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12723 if (mng_info->ping_exclude_sRGB != MagickFalse)
12724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12726 if (mng_info->ping_exclude_tEXt != MagickFalse)
12727 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12729 if (mng_info->ping_exclude_tIME != MagickFalse)
12730 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12732 if (mng_info->ping_exclude_tRNS != MagickFalse)
12733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12735 if (mng_info->ping_exclude_vpAg != MagickFalse)
12736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12738 if (mng_info->ping_exclude_zCCP != MagickFalse)
12739 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12741 if (mng_info->ping_exclude_zTXt != MagickFalse)
12742 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12746 mng_info->need_blob = MagickTrue;
12748 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12750 MngInfoFreeStruct(mng_info,&have_mng_structure);
12752 if (logging != MagickFalse)
12753 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12758 #if defined(JNG_SUPPORTED)
12760 /* Write one JNG image */
12761 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12762 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12783 jng_alpha_compression_method,
12784 jng_alpha_sample_depth,
12792 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12793 " Enter WriteOneJNGImage()");
12795 blob=(unsigned char *) NULL;
12796 jpeg_image=(Image *) NULL;
12797 jpeg_image_info=(ImageInfo *) NULL;
12801 transparent=image_info->type==GrayscaleAlphaType ||
12802 image_info->type==TrueColorAlphaType ||
12803 image->alpha_trait != UndefinedPixelTrait;
12805 jng_alpha_sample_depth = 0;
12807 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12809 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12811 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12812 image_info->quality;
12814 if (jng_alpha_quality >= 1000)
12815 jng_alpha_quality /= 1000;
12819 if (transparent != 0)
12823 /* Create JPEG blob, image, and image_info */
12824 if (logging != MagickFalse)
12825 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12826 " Creating jpeg_image_info for alpha.");
12828 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12830 if (jpeg_image_info == (ImageInfo *) NULL)
12831 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12833 if (logging != MagickFalse)
12834 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12835 " Creating jpeg_image.");
12837 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12838 if (jpeg_image == (Image *) NULL)
12839 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12840 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12841 jpeg_image->alpha_trait=UndefinedPixelTrait;
12842 jpeg_image->quality=jng_alpha_quality;
12843 jpeg_image_info->type=GrayscaleType;
12844 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12845 (void) AcquireUniqueFilename(jpeg_image->filename);
12846 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12847 "%s",jpeg_image->filename);
12851 jng_alpha_compression_method=0;
12853 jng_alpha_sample_depth=0;
12856 /* To do: check bit depth of PNG alpha channel */
12858 /* Check if image is grayscale. */
12859 if (image_info->type != TrueColorAlphaType && image_info->type !=
12860 TrueColorType && SetImageGray(image,exception))
12863 if (logging != MagickFalse)
12865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12866 " JNG Quality = %d",(int) jng_quality);
12867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12868 " JNG Color Type = %d",jng_color_type);
12869 if (transparent != 0)
12871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12872 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12873 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12874 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12876 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12880 if (transparent != 0)
12882 if (jng_alpha_compression_method==0)
12887 /* Encode alpha as a grayscale PNG blob */
12888 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12890 if (status == MagickFalse)
12891 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12893 if (logging != MagickFalse)
12894 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12895 " Creating PNG blob.");
12897 (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12899 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12900 jpeg_image_info->interlace=NoInterlace;
12902 /* Exclude all ancillary chunks */
12903 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12905 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12906 &length,exception);
12908 /* Retrieve sample depth used */
12909 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12910 if (value != (char *) NULL)
12911 jng_alpha_sample_depth= (unsigned int) value[0];
12915 /* Encode alpha as a grayscale JPEG blob */
12917 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12919 if (status == MagickFalse)
12920 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12922 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12924 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12925 jpeg_image_info->interlace=NoInterlace;
12926 if (logging != MagickFalse)
12927 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12928 " Creating blob.");
12929 blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12930 jpeg_image,&length,
12932 jng_alpha_sample_depth=8;
12934 if (logging != MagickFalse)
12935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12936 " Successfully read jpeg_image into a blob, length=%.20g.",
12940 /* Destroy JPEG image and image_info */
12941 jpeg_image=DestroyImage(jpeg_image);
12942 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12943 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12946 /* Write JHDR chunk */
12947 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12948 PNGType(chunk,mng_JHDR);
12949 LogPNGChunk(logging,mng_JHDR,16L);
12950 PNGLong(chunk+4,(png_uint_32) image->columns);
12951 PNGLong(chunk+8,(png_uint_32) image->rows);
12952 chunk[12]=jng_color_type;
12953 chunk[13]=8; /* sample depth */
12954 chunk[14]=8; /*jng_image_compression_method */
12955 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12956 chunk[16]=jng_alpha_sample_depth;
12957 chunk[17]=jng_alpha_compression_method;
12958 chunk[18]=0; /*jng_alpha_filter_method */
12959 chunk[19]=0; /*jng_alpha_interlace_method */
12960 (void) WriteBlob(image,20,chunk);
12961 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12962 if (logging != MagickFalse)
12964 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12965 " JNG width:%15lu",(unsigned long) image->columns);
12967 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12968 " JNG height:%14lu",(unsigned long) image->rows);
12970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12971 " JNG color type:%10d",jng_color_type);
12973 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12974 " JNG sample depth:%8d",8);
12976 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12977 " JNG compression:%9d",8);
12979 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12980 " JNG interlace:%11d",0);
12982 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12983 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12985 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12986 " JNG alpha compression:%3d",jng_alpha_compression_method);
12988 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12989 " JNG alpha filter:%8d",0);
12991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12992 " JNG alpha interlace:%5d",0);
12995 /* Write any JNG-chunk-b profiles */
12996 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12999 Write leading ancillary chunks
13002 if (transparent != 0)
13005 Write JNG bKGD chunk
13016 if (jng_color_type == 8 || jng_color_type == 12)
13020 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
13021 PNGType(chunk,mng_bKGD);
13022 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
13023 red=ScaleQuantumToChar(image->background_color.red);
13024 green=ScaleQuantumToChar(image->background_color.green);
13025 blue=ScaleQuantumToChar(image->background_color.blue);
13032 (void) WriteBlob(image,(size_t) num_bytes,chunk);
13033 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13036 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13039 Write JNG sRGB chunk
13041 (void) WriteBlobMSBULong(image,1L);
13042 PNGType(chunk,mng_sRGB);
13043 LogPNGChunk(logging,mng_sRGB,1L);
13045 if (image->rendering_intent != UndefinedIntent)
13046 chunk[4]=(unsigned char)
13047 Magick_RenderingIntent_to_PNG_RenderingIntent(
13048 (image->rendering_intent));
13051 chunk[4]=(unsigned char)
13052 Magick_RenderingIntent_to_PNG_RenderingIntent(
13053 (PerceptualIntent));
13055 (void) WriteBlob(image,5,chunk);
13056 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13060 if (image->gamma != 0.0)
13063 Write JNG gAMA chunk
13065 (void) WriteBlobMSBULong(image,4L);
13066 PNGType(chunk,mng_gAMA);
13067 LogPNGChunk(logging,mng_gAMA,4L);
13068 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13069 (void) WriteBlob(image,8,chunk);
13070 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13073 if ((mng_info->equal_chrms == MagickFalse) &&
13074 (image->chromaticity.red_primary.x != 0.0))
13080 Write JNG cHRM chunk
13082 (void) WriteBlobMSBULong(image,32L);
13083 PNGType(chunk,mng_cHRM);
13084 LogPNGChunk(logging,mng_cHRM,32L);
13085 primary=image->chromaticity.white_point;
13086 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13087 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13088 primary=image->chromaticity.red_primary;
13089 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13090 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13091 primary=image->chromaticity.green_primary;
13092 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13093 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13094 primary=image->chromaticity.blue_primary;
13095 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13096 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13097 (void) WriteBlob(image,36,chunk);
13098 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13102 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13105 Write JNG pHYs chunk
13107 (void) WriteBlobMSBULong(image,9L);
13108 PNGType(chunk,mng_pHYs);
13109 LogPNGChunk(logging,mng_pHYs,9L);
13110 if (image->units == PixelsPerInchResolution)
13112 PNGLong(chunk+4,(png_uint_32)
13113 (image->resolution.x*100.0/2.54+0.5));
13115 PNGLong(chunk+8,(png_uint_32)
13116 (image->resolution.y*100.0/2.54+0.5));
13123 if (image->units == PixelsPerCentimeterResolution)
13125 PNGLong(chunk+4,(png_uint_32)
13126 (image->resolution.x*100.0+0.5));
13128 PNGLong(chunk+8,(png_uint_32)
13129 (image->resolution.y*100.0+0.5));
13136 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13137 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13141 (void) WriteBlob(image,13,chunk);
13142 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13145 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13148 Write JNG oFFs chunk
13150 (void) WriteBlobMSBULong(image,9L);
13151 PNGType(chunk,mng_oFFs);
13152 LogPNGChunk(logging,mng_oFFs,9L);
13153 PNGsLong(chunk+4,(ssize_t) (image->page.x));
13154 PNGsLong(chunk+8,(ssize_t) (image->page.y));
13156 (void) WriteBlob(image,13,chunk);
13157 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13159 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
13161 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
13162 PNGType(chunk,mng_vpAg);
13163 LogPNGChunk(logging,mng_vpAg,9L);
13164 PNGLong(chunk+4,(png_uint_32) image->page.width);
13165 PNGLong(chunk+8,(png_uint_32) image->page.height);
13166 chunk[12]=0; /* unit = pixels */
13167 (void) WriteBlob(image,13,chunk);
13168 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13171 if (transparent != 0)
13173 if (jng_alpha_compression_method==0)
13181 /* Write IDAT chunk header */
13182 if (logging != MagickFalse)
13183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13184 " Write IDAT chunks from blob, length=%.20g.",(double)
13187 /* Copy IDAT chunks */
13190 for (i=8; i<(ssize_t) length; i+=len+12)
13192 len=(size_t) (*p) << 24;
13193 len|=(size_t) (*(p+1)) << 16;
13194 len|=(size_t) (*(p+2)) << 8;
13195 len|=(size_t) (*(p+3));
13198 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13200 /* Found an IDAT chunk. */
13201 (void) WriteBlobMSBULong(image,len);
13202 LogPNGChunk(logging,mng_IDAT,len);
13203 (void) WriteBlob(image,len+4,p);
13204 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13209 if (logging != MagickFalse)
13210 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13211 " Skipping %c%c%c%c chunk, length=%.20g.",
13212 *(p),*(p+1),*(p+2),*(p+3),(double) len);
13217 else if (length != 0)
13219 /* Write JDAA chunk header */
13220 if (logging != MagickFalse)
13221 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13222 " Write JDAA chunk, length=%.20g.",(double) length);
13223 (void) WriteBlobMSBULong(image,(size_t) length);
13224 PNGType(chunk,mng_JDAA);
13225 LogPNGChunk(logging,mng_JDAA,length);
13226 /* Write JDAT chunk(s) data */
13227 (void) WriteBlob(image,4,chunk);
13228 (void) WriteBlob(image,length,blob);
13229 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13232 blob=(unsigned char *) RelinquishMagickMemory(blob);
13235 /* Encode image as a JPEG blob */
13236 if (logging != MagickFalse)
13237 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13238 " Creating jpeg_image_info.");
13239 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13240 if (jpeg_image_info == (ImageInfo *) NULL)
13241 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13243 if (logging != MagickFalse)
13244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13245 " Creating jpeg_image.");
13247 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13248 if (jpeg_image == (Image *) NULL)
13249 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13250 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13252 (void) AcquireUniqueFilename(jpeg_image->filename);
13253 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13254 jpeg_image->filename);
13256 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13259 if (logging != MagickFalse)
13260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13261 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13262 (double) jpeg_image->rows);
13264 if (status == MagickFalse)
13265 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13267 if (jng_color_type == 8 || jng_color_type == 12)
13268 jpeg_image_info->type=GrayscaleType;
13270 jpeg_image_info->quality=jng_quality;
13271 jpeg_image->quality=jng_quality;
13272 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13273 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13275 if (logging != MagickFalse)
13276 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13277 " Creating blob.");
13279 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13282 if (logging != MagickFalse)
13284 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13285 " Successfully read jpeg_image into a blob, length=%.20g.",
13288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13289 " Write JDAT chunk, length=%.20g.",(double) length);
13292 /* Write JDAT chunk(s) */
13293 (void) WriteBlobMSBULong(image,(size_t) length);
13294 PNGType(chunk,mng_JDAT);
13295 LogPNGChunk(logging,mng_JDAT,length);
13296 (void) WriteBlob(image,4,chunk);
13297 (void) WriteBlob(image,length,blob);
13298 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13300 jpeg_image=DestroyImage(jpeg_image);
13301 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13302 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13303 blob=(unsigned char *) RelinquishMagickMemory(blob);
13305 /* Write any JNG-chunk-e profiles */
13306 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
13308 /* Write IEND chunk */
13309 (void) WriteBlobMSBULong(image,0L);
13310 PNGType(chunk,mng_IEND);
13311 LogPNGChunk(logging,mng_IEND,0);
13312 (void) WriteBlob(image,4,chunk);
13313 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13315 if (logging != MagickFalse)
13316 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13317 " exit WriteOneJNGImage()");
13323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13327 % W r i t e J N G I m a g e %
13331 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13333 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13335 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
13337 % The format of the WriteJNGImage method is:
13339 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13340 % Image *image,ExceptionInfo *exception)
13342 % A description of each parameter follows:
13344 % o image_info: the image info.
13346 % o image: The image.
13348 % o exception: return any errors or warnings in this structure.
13350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13352 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13353 Image *image, ExceptionInfo *exception)
13356 have_mng_structure,
13366 assert(image_info != (const ImageInfo *) NULL);
13367 assert(image_info->signature == MagickCoreSignature);
13368 assert(image != (Image *) NULL);
13369 assert(image->signature == MagickCoreSignature);
13370 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13371 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13372 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13373 if (status == MagickFalse)
13375 if ((image->columns > 65535UL) || (image->rows > 65535UL))
13376 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13379 Allocate a MngInfo structure.
13381 have_mng_structure=MagickFalse;
13382 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13383 if (mng_info == (MngInfo *) NULL)
13384 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13386 Initialize members of the MngInfo structure.
13388 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13389 mng_info->image=image;
13390 have_mng_structure=MagickTrue;
13392 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13394 status=WriteOneJNGImage(mng_info,image_info,image,exception);
13395 (void) CloseBlob(image);
13397 (void) CatchImageException(image);
13398 MngInfoFreeStruct(mng_info,&have_mng_structure);
13399 if (logging != MagickFalse)
13400 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13405 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13406 Image *image, ExceptionInfo *exception)
13412 have_mng_structure,
13415 volatile MagickBooleanType
13427 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13428 defined(PNG_MNG_FEATURES_SUPPORTED)
13431 all_images_are_gray,
13441 volatile unsigned int
13452 #if (PNG_LIBPNG_VER < 10200)
13453 if (image_info->verbose)
13454 printf("Your PNG library (libpng-%s) is rather old.\n",
13455 PNG_LIBPNG_VER_STRING);
13461 assert(image_info != (const ImageInfo *) NULL);
13462 assert(image_info->signature == MagickCoreSignature);
13463 assert(image != (Image *) NULL);
13464 assert(image->signature == MagickCoreSignature);
13465 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13466 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13467 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13468 if (status == MagickFalse)
13472 Allocate a MngInfo structure.
13474 have_mng_structure=MagickFalse;
13475 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13476 if (mng_info == (MngInfo *) NULL)
13477 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13479 Initialize members of the MngInfo structure.
13481 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13482 mng_info->image=image;
13483 have_mng_structure=MagickTrue;
13484 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13487 * See if user has requested a specific PNG subformat to be used
13488 * for all of the PNGs in the MNG being written, e.g.,
13490 * convert *.png png8:animation.mng
13492 * To do: check -define png:bit_depth and png:color_type as well,
13493 * or perhaps use mng:bit_depth and mng:color_type instead for
13497 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13498 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13499 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13501 write_jng=MagickFalse;
13502 if (image_info->compression == JPEGCompression)
13503 write_jng=MagickTrue;
13505 mng_info->adjoin=image_info->adjoin &&
13506 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13508 if (logging != MagickFalse)
13510 /* Log some info about the input */
13514 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13515 " Checking input image(s)\n"
13516 " Image_info depth: %.20g, Type: %d",
13517 (double) image_info->depth, image_info->type);
13520 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13523 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13524 " Scene: %.20g\n, Image depth: %.20g",
13525 (double) scene++, (double) p->depth);
13527 if (p->alpha_trait != UndefinedPixelTrait)
13528 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13532 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13535 if (p->storage_class == PseudoClass)
13536 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13537 " Storage class: PseudoClass");
13540 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13541 " Storage class: DirectClass");
13544 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13545 " Number of colors: %.20g",(double) p->colors);
13548 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13549 " Number of colors: unspecified");
13551 if (mng_info->adjoin == MagickFalse)
13556 use_global_plte=MagickFalse;
13557 all_images_are_gray=MagickFalse;
13558 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13559 need_local_plte=MagickTrue;
13561 need_defi=MagickFalse;
13562 need_matte=MagickFalse;
13563 mng_info->framing_mode=1;
13564 mng_info->old_framing_mode=1;
13567 if (image_info->page != (char *) NULL)
13570 Determine image bounding box.
13572 SetGeometry(image,&mng_info->page);
13573 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13574 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13589 mng_info->page=image->page;
13590 need_geom=MagickTrue;
13591 if (mng_info->page.width || mng_info->page.height)
13592 need_geom=MagickFalse;
13594 Check all the scenes.
13596 initial_delay=image->delay;
13597 need_iterations=MagickFalse;
13598 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13599 mng_info->equal_physs=MagickTrue,
13600 mng_info->equal_gammas=MagickTrue;
13601 mng_info->equal_srgbs=MagickTrue;
13602 mng_info->equal_backgrounds=MagickTrue;
13604 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13605 defined(PNG_MNG_FEATURES_SUPPORTED)
13606 all_images_are_gray=MagickTrue;
13607 mng_info->equal_palettes=MagickFalse;
13608 need_local_plte=MagickFalse;
13610 for (next_image=image; next_image != (Image *) NULL; )
13614 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13615 mng_info->page.width=next_image->columns+next_image->page.x;
13617 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13618 mng_info->page.height=next_image->rows+next_image->page.y;
13621 if (next_image->page.x || next_image->page.y)
13622 need_defi=MagickTrue;
13624 if (next_image->alpha_trait != UndefinedPixelTrait)
13625 need_matte=MagickTrue;
13627 if ((int) next_image->dispose >= BackgroundDispose)
13628 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13629 next_image->page.x || next_image->page.y ||
13630 ((next_image->columns < mng_info->page.width) &&
13631 (next_image->rows < mng_info->page.height)))
13632 mng_info->need_fram=MagickTrue;
13634 if (next_image->iterations)
13635 need_iterations=MagickTrue;
13637 final_delay=next_image->delay;
13639 if (final_delay != initial_delay || final_delay > 1UL*
13640 next_image->ticks_per_second)
13641 mng_info->need_fram=1;
13643 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13644 defined(PNG_MNG_FEATURES_SUPPORTED)
13646 check for global palette possibility.
13648 if (image->alpha_trait != UndefinedPixelTrait)
13649 need_local_plte=MagickTrue;
13651 if (need_local_plte == 0)
13653 if (SetImageGray(image,exception) == MagickFalse)
13654 all_images_are_gray=MagickFalse;
13655 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13656 if (use_global_plte == 0)
13657 use_global_plte=mng_info->equal_palettes;
13658 need_local_plte=!mng_info->equal_palettes;
13661 if (GetNextImageInList(next_image) != (Image *) NULL)
13663 if (next_image->background_color.red !=
13664 next_image->next->background_color.red ||
13665 next_image->background_color.green !=
13666 next_image->next->background_color.green ||
13667 next_image->background_color.blue !=
13668 next_image->next->background_color.blue)
13669 mng_info->equal_backgrounds=MagickFalse;
13671 if (next_image->gamma != next_image->next->gamma)
13672 mng_info->equal_gammas=MagickFalse;
13674 if (next_image->rendering_intent !=
13675 next_image->next->rendering_intent)
13676 mng_info->equal_srgbs=MagickFalse;
13678 if ((next_image->units != next_image->next->units) ||
13679 (next_image->resolution.x != next_image->next->resolution.x) ||
13680 (next_image->resolution.y != next_image->next->resolution.y))
13681 mng_info->equal_physs=MagickFalse;
13683 if (mng_info->equal_chrms)
13685 if (next_image->chromaticity.red_primary.x !=
13686 next_image->next->chromaticity.red_primary.x ||
13687 next_image->chromaticity.red_primary.y !=
13688 next_image->next->chromaticity.red_primary.y ||
13689 next_image->chromaticity.green_primary.x !=
13690 next_image->next->chromaticity.green_primary.x ||
13691 next_image->chromaticity.green_primary.y !=
13692 next_image->next->chromaticity.green_primary.y ||
13693 next_image->chromaticity.blue_primary.x !=
13694 next_image->next->chromaticity.blue_primary.x ||
13695 next_image->chromaticity.blue_primary.y !=
13696 next_image->next->chromaticity.blue_primary.y ||
13697 next_image->chromaticity.white_point.x !=
13698 next_image->next->chromaticity.white_point.x ||
13699 next_image->chromaticity.white_point.y !=
13700 next_image->next->chromaticity.white_point.y)
13701 mng_info->equal_chrms=MagickFalse;
13705 next_image=GetNextImageInList(next_image);
13707 if (image_count < 2)
13709 mng_info->equal_backgrounds=MagickFalse;
13710 mng_info->equal_chrms=MagickFalse;
13711 mng_info->equal_gammas=MagickFalse;
13712 mng_info->equal_srgbs=MagickFalse;
13713 mng_info->equal_physs=MagickFalse;
13714 use_global_plte=MagickFalse;
13715 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13716 need_local_plte=MagickTrue;
13718 need_iterations=MagickFalse;
13721 if (mng_info->need_fram == MagickFalse)
13724 Only certain framing rates 100/n are exactly representable without
13725 the FRAM chunk but we'll allow some slop in VLC files
13727 if (final_delay == 0)
13729 if (need_iterations != MagickFalse)
13732 It's probably a GIF with loop; don't run it *too* fast.
13734 if (mng_info->adjoin)
13737 (void) ThrowMagickException(exception,GetMagickModule(),
13739 "input has zero delay between all frames; assuming",
13744 mng_info->ticks_per_second=0;
13746 if (final_delay != 0)
13747 mng_info->ticks_per_second=(png_uint_32)
13748 (image->ticks_per_second/final_delay);
13749 if (final_delay > 50)
13750 mng_info->ticks_per_second=2;
13752 if (final_delay > 75)
13753 mng_info->ticks_per_second=1;
13755 if (final_delay > 125)
13756 mng_info->need_fram=MagickTrue;
13758 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13759 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13760 (final_delay != 25) && (final_delay != 50) &&
13761 (final_delay != (size_t) image->ticks_per_second))
13762 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13765 if (mng_info->need_fram != MagickFalse)
13766 mng_info->ticks_per_second=image->ticks_per_second;
13768 If pseudocolor, we should also check to see if all the
13769 palettes are identical and write a global PLTE if they are.
13773 Write the MNG version 1.0 signature and MHDR chunk.
13775 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13776 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13777 PNGType(chunk,mng_MHDR);
13778 LogPNGChunk(logging,mng_MHDR,28L);
13779 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13780 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13781 PNGLong(chunk+12,mng_info->ticks_per_second);
13782 PNGLong(chunk+16,0L); /* layer count=unknown */
13783 PNGLong(chunk+20,0L); /* frame count=unknown */
13784 PNGLong(chunk+24,0L); /* play time=unknown */
13789 if (need_defi || mng_info->need_fram || use_global_plte)
13790 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13793 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13798 if (need_defi || mng_info->need_fram || use_global_plte)
13799 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13802 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13810 if (need_defi || mng_info->need_fram || use_global_plte)
13811 PNGLong(chunk+28,11L); /* simplicity=LC */
13814 PNGLong(chunk+28,9L); /* simplicity=VLC */
13819 if (need_defi || mng_info->need_fram || use_global_plte)
13820 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13823 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13826 (void) WriteBlob(image,32,chunk);
13827 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13828 option=GetImageOption(image_info,"mng:need-cacheoff");
13829 if (option != (const char *) NULL)
13834 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13836 PNGType(chunk,mng_nEED);
13837 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13838 (void) WriteBlobMSBULong(image,(size_t) length);
13839 LogPNGChunk(logging,mng_nEED,(size_t) length);
13841 (void) WriteBlob(image,length,chunk);
13842 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13844 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13845 (GetNextImageInList(image) != (Image *) NULL) &&
13846 (image->iterations != 1))
13849 Write MNG TERM chunk
13851 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13852 PNGType(chunk,mng_TERM);
13853 LogPNGChunk(logging,mng_TERM,10L);
13854 chunk[4]=3; /* repeat animation */
13855 chunk[5]=0; /* show last frame when done */
13856 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13857 final_delay/MagickMax(image->ticks_per_second,1)));
13859 if (image->iterations == 0)
13860 PNGLong(chunk+10,PNG_UINT_31_MAX);
13863 PNGLong(chunk+10,(png_uint_32) image->iterations);
13865 if (logging != MagickFalse)
13867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13868 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13869 final_delay/MagickMax(image->ticks_per_second,1)));
13871 if (image->iterations == 0)
13872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13873 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13876 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13877 " Image iterations: %.20g",(double) image->iterations);
13879 (void) WriteBlob(image,14,chunk);
13880 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13883 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13885 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13886 mng_info->equal_srgbs)
13889 Write MNG sRGB chunk
13891 (void) WriteBlobMSBULong(image,1L);
13892 PNGType(chunk,mng_sRGB);
13893 LogPNGChunk(logging,mng_sRGB,1L);
13895 if (image->rendering_intent != UndefinedIntent)
13896 chunk[4]=(unsigned char)
13897 Magick_RenderingIntent_to_PNG_RenderingIntent(
13898 (image->rendering_intent));
13901 chunk[4]=(unsigned char)
13902 Magick_RenderingIntent_to_PNG_RenderingIntent(
13903 (PerceptualIntent));
13905 (void) WriteBlob(image,5,chunk);
13906 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13907 mng_info->have_write_global_srgb=MagickTrue;
13912 if (image->gamma && mng_info->equal_gammas)
13915 Write MNG gAMA chunk
13917 (void) WriteBlobMSBULong(image,4L);
13918 PNGType(chunk,mng_gAMA);
13919 LogPNGChunk(logging,mng_gAMA,4L);
13920 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13921 (void) WriteBlob(image,8,chunk);
13922 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13923 mng_info->have_write_global_gama=MagickTrue;
13925 if (mng_info->equal_chrms)
13931 Write MNG cHRM chunk
13933 (void) WriteBlobMSBULong(image,32L);
13934 PNGType(chunk,mng_cHRM);
13935 LogPNGChunk(logging,mng_cHRM,32L);
13936 primary=image->chromaticity.white_point;
13937 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13938 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13939 primary=image->chromaticity.red_primary;
13940 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13941 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13942 primary=image->chromaticity.green_primary;
13943 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13944 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13945 primary=image->chromaticity.blue_primary;
13946 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13947 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13948 (void) WriteBlob(image,36,chunk);
13949 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13950 mng_info->have_write_global_chrm=MagickTrue;
13953 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13956 Write MNG pHYs chunk
13958 (void) WriteBlobMSBULong(image,9L);
13959 PNGType(chunk,mng_pHYs);
13960 LogPNGChunk(logging,mng_pHYs,9L);
13962 if (image->units == PixelsPerInchResolution)
13964 PNGLong(chunk+4,(png_uint_32)
13965 (image->resolution.x*100.0/2.54+0.5));
13967 PNGLong(chunk+8,(png_uint_32)
13968 (image->resolution.y*100.0/2.54+0.5));
13975 if (image->units == PixelsPerCentimeterResolution)
13977 PNGLong(chunk+4,(png_uint_32)
13978 (image->resolution.x*100.0+0.5));
13980 PNGLong(chunk+8,(png_uint_32)
13981 (image->resolution.y*100.0+0.5));
13988 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13989 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13993 (void) WriteBlob(image,13,chunk);
13994 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13997 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13998 or does not cover the entire frame.
14000 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
14001 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
14002 (image->page.width+image->page.x < mng_info->page.width))
14003 || (image->page.height && (image->page.height+image->page.y
14004 < mng_info->page.height))))
14006 (void) WriteBlobMSBULong(image,6L);
14007 PNGType(chunk,mng_BACK);
14008 LogPNGChunk(logging,mng_BACK,6L);
14009 red=ScaleQuantumToShort(image->background_color.red);
14010 green=ScaleQuantumToShort(image->background_color.green);
14011 blue=ScaleQuantumToShort(image->background_color.blue);
14012 PNGShort(chunk+4,red);
14013 PNGShort(chunk+6,green);
14014 PNGShort(chunk+8,blue);
14015 (void) WriteBlob(image,10,chunk);
14016 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14017 if (mng_info->equal_backgrounds)
14019 (void) WriteBlobMSBULong(image,6L);
14020 PNGType(chunk,mng_bKGD);
14021 LogPNGChunk(logging,mng_bKGD,6L);
14022 (void) WriteBlob(image,10,chunk);
14023 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14027 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
14028 if ((need_local_plte == MagickFalse) &&
14029 (image->storage_class == PseudoClass) &&
14030 (all_images_are_gray == MagickFalse))
14036 Write MNG PLTE chunk
14038 data_length=3*image->colors;
14039 (void) WriteBlobMSBULong(image,data_length);
14040 PNGType(chunk,mng_PLTE);
14041 LogPNGChunk(logging,mng_PLTE,data_length);
14043 for (i=0; i < (ssize_t) image->colors; i++)
14045 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14046 image->colormap[i].red) & 0xff);
14047 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14048 image->colormap[i].green) & 0xff);
14049 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14050 image->colormap[i].blue) & 0xff);
14053 (void) WriteBlob(image,data_length+4,chunk);
14054 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14055 mng_info->have_write_global_plte=MagickTrue;
14061 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14062 defined(PNG_MNG_FEATURES_SUPPORTED)
14063 mng_info->equal_palettes=MagickFalse;
14067 if (mng_info->adjoin)
14069 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14070 defined(PNG_MNG_FEATURES_SUPPORTED)
14072 If we aren't using a global palette for the entire MNG, check to
14073 see if we can use one for two or more consecutive images.
14075 if (need_local_plte && use_global_plte && !all_images_are_gray)
14077 if (mng_info->IsPalette)
14080 When equal_palettes is true, this image has the same palette
14081 as the previous PseudoClass image
14083 mng_info->have_write_global_plte=mng_info->equal_palettes;
14084 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14085 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14088 Write MNG PLTE chunk
14093 data_length=3*image->colors;
14094 (void) WriteBlobMSBULong(image,data_length);
14095 PNGType(chunk,mng_PLTE);
14096 LogPNGChunk(logging,mng_PLTE,data_length);
14098 for (i=0; i < (ssize_t) image->colors; i++)
14100 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14101 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14102 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14105 (void) WriteBlob(image,data_length+4,chunk);
14106 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14107 (uInt) (data_length+4)));
14108 mng_info->have_write_global_plte=MagickTrue;
14112 mng_info->have_write_global_plte=MagickFalse;
14123 previous_x=mng_info->page.x;
14124 previous_y=mng_info->page.y;
14131 mng_info->page=image->page;
14132 if ((mng_info->page.x != previous_x) ||
14133 (mng_info->page.y != previous_y))
14135 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
14136 PNGType(chunk,mng_DEFI);
14137 LogPNGChunk(logging,mng_DEFI,12L);
14138 chunk[4]=0; /* object 0 MSB */
14139 chunk[5]=0; /* object 0 LSB */
14140 chunk[6]=0; /* visible */
14141 chunk[7]=0; /* abstract */
14142 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14143 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14144 (void) WriteBlob(image,16,chunk);
14145 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14150 mng_info->write_mng=write_mng;
14152 if ((int) image->dispose >= 3)
14153 mng_info->framing_mode=3;
14155 if (mng_info->need_fram && mng_info->adjoin &&
14156 ((image->delay != mng_info->delay) ||
14157 (mng_info->framing_mode != mng_info->old_framing_mode)))
14159 if (image->delay == mng_info->delay)
14162 Write a MNG FRAM chunk with the new framing mode.
14164 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
14165 PNGType(chunk,mng_FRAM);
14166 LogPNGChunk(logging,mng_FRAM,1L);
14167 chunk[4]=(unsigned char) mng_info->framing_mode;
14168 (void) WriteBlob(image,5,chunk);
14169 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14174 Write a MNG FRAM chunk with the delay.
14176 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
14177 PNGType(chunk,mng_FRAM);
14178 LogPNGChunk(logging,mng_FRAM,10L);
14179 chunk[4]=(unsigned char) mng_info->framing_mode;
14180 chunk[5]=0; /* frame name separator (no name) */
14181 chunk[6]=2; /* flag for changing default delay */
14182 chunk[7]=0; /* flag for changing frame timeout */
14183 chunk[8]=0; /* flag for changing frame clipping */
14184 chunk[9]=0; /* flag for changing frame sync_id */
14185 PNGLong(chunk+10,(png_uint_32)
14186 ((mng_info->ticks_per_second*
14187 image->delay)/MagickMax(image->ticks_per_second,1)));
14188 (void) WriteBlob(image,14,chunk);
14189 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14190 mng_info->delay=(png_uint_32) image->delay;
14192 mng_info->old_framing_mode=mng_info->framing_mode;
14195 #if defined(JNG_SUPPORTED)
14196 if (image_info->compression == JPEGCompression)
14201 if (logging != MagickFalse)
14202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14203 " Writing JNG object.");
14204 /* To do: specify the desired alpha compression method. */
14205 write_info=CloneImageInfo(image_info);
14206 write_info->compression=UndefinedCompression;
14207 status=WriteOneJNGImage(mng_info,write_info,image,exception);
14208 write_info=DestroyImageInfo(write_info);
14213 if (logging != MagickFalse)
14214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14215 " Writing PNG object.");
14217 mng_info->need_blob = MagickFalse;
14218 mng_info->ping_preserve_colormap = MagickFalse;
14220 /* We don't want any ancillary chunks written */
14221 mng_info->ping_exclude_bKGD=MagickTrue;
14222 mng_info->ping_exclude_caNv=MagickTrue;
14223 mng_info->ping_exclude_cHRM=MagickTrue;
14224 mng_info->ping_exclude_date=MagickTrue;
14225 mng_info->ping_exclude_EXIF=MagickTrue;
14226 mng_info->ping_exclude_gAMA=MagickTrue;
14227 mng_info->ping_exclude_iCCP=MagickTrue;
14228 /* mng_info->ping_exclude_iTXt=MagickTrue; */
14229 mng_info->ping_exclude_oFFs=MagickTrue;
14230 mng_info->ping_exclude_pHYs=MagickTrue;
14231 mng_info->ping_exclude_sRGB=MagickTrue;
14232 mng_info->ping_exclude_tEXt=MagickTrue;
14233 mng_info->ping_exclude_tRNS=MagickTrue;
14234 mng_info->ping_exclude_vpAg=MagickTrue;
14235 mng_info->ping_exclude_zCCP=MagickTrue;
14236 mng_info->ping_exclude_zTXt=MagickTrue;
14238 status=WriteOnePNGImage(mng_info,image_info,image,exception);
14241 if (status == MagickFalse)
14243 MngInfoFreeStruct(mng_info,&have_mng_structure);
14244 (void) CloseBlob(image);
14245 return(MagickFalse);
14247 (void) CatchImageException(image);
14248 if (GetNextImageInList(image) == (Image *) NULL)
14250 image=SyncNextImageInList(image);
14251 status=SetImageProgress(image,SaveImagesTag,scene++,
14252 GetImageListLength(image));
14254 if (status == MagickFalse)
14257 } while (mng_info->adjoin);
14261 while (GetPreviousImageInList(image) != (Image *) NULL)
14262 image=GetPreviousImageInList(image);
14264 Write the MEND chunk.
14266 (void) WriteBlobMSBULong(image,0x00000000L);
14267 PNGType(chunk,mng_MEND);
14268 LogPNGChunk(logging,mng_MEND,0L);
14269 (void) WriteBlob(image,4,chunk);
14270 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14273 Relinquish resources.
14275 (void) CloseBlob(image);
14276 MngInfoFreeStruct(mng_info,&have_mng_structure);
14278 if (logging != MagickFalse)
14279 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14281 return(MagickTrue);
14283 #else /* PNG_LIBPNG_VER > 10011 */
14285 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14289 printf("Your PNG library is too old: You have libpng-%s\n",
14290 PNG_LIBPNG_VER_STRING);
14292 ThrowBinaryException(CoderError,"PNG library is too old",
14293 image_info->filename);
14296 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14299 return(WritePNGImage(image_info,image));
14301 #endif /* PNG_LIBPNG_VER > 10011 */