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 MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1562 if (mng_info == (MngInfo *) NULL)
1563 return((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 return((MngInfo *) RelinquishMagickMemory(mng_info));
1575 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1581 if (box.left < box2.left)
1584 if (box.top < box2.top)
1587 if (box.right > box2.right)
1588 box.right=box2.right;
1590 if (box.bottom > box2.bottom)
1591 box.bottom=box2.bottom;
1596 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1603 Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1605 box.left=(ssize_t) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1606 box.right=(ssize_t) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1607 box.top=(ssize_t) ((p[8] << 24) | (p[9] << 16) | (p[10] << 8) | p[11]);
1608 box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1609 if (delta_type != 0)
1611 box.left+=previous_box.left;
1612 box.right+=previous_box.right;
1613 box.top+=previous_box.top;
1614 box.bottom+=previous_box.bottom;
1620 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1626 Read two ssize_ts from CLON, MOVE or PAST chunk
1628 pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1629 pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1631 if (delta_type != 0)
1633 pair.a+=previous_pair.a;
1634 pair.b+=previous_pair.b;
1640 static long mng_get_long(unsigned char *p)
1642 return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1645 typedef struct _PNGErrorInfo
1654 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1665 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1666 image=error_info->image;
1667 exception=error_info->exception;
1669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1670 " libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1672 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1673 "`%s'",image->filename);
1675 #if (PNG_LIBPNG_VER < 10500)
1676 /* A warning about deprecated use of jmpbuf here is unavoidable if you
1677 * are building with libpng-1.4.x and can be ignored.
1679 longjmp(ping->jmpbuf,1);
1681 png_longjmp(ping,1);
1685 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1696 if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1697 png_error(ping, message);
1699 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1700 image=error_info->image;
1701 exception=error_info->exception;
1702 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1703 " libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1705 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1706 message,"`%s'",image->filename);
1709 #ifdef PNG_USER_MEM_SUPPORTED
1710 #if PNG_LIBPNG_VER >= 10400
1711 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1713 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1717 return((png_voidp) AcquireMagickMemory((size_t) size));
1721 Free a pointer. It is removed from the list at the same time.
1723 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1726 ptr=RelinquishMagickMemory(ptr);
1727 return((png_free_ptr) NULL);
1731 #if defined(__cplusplus) || defined(c_plusplus)
1736 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1737 const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1742 register unsigned char
1756 unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1757 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1758 0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,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,0, 0,0,0,0,0,0,0,10,11,12,
1764 /* look for newline */
1768 /* look for length */
1769 while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1772 length=(png_uint_32) StringToLong(sp);
1774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1775 " length: %lu",(unsigned long) length);
1777 while (*sp != ' ' && *sp != '\n')
1780 /* allocate space */
1783 png_warning(ping,"invalid profile length");
1784 return(MagickFalse);
1787 profile=BlobToStringInfo((const void *) NULL,length);
1789 if (profile == (StringInfo *) NULL)
1791 png_warning(ping, "unable to copy profile");
1792 return(MagickFalse);
1795 /* copy profile, skipping white space and column 1 "=" signs */
1796 dp=GetStringInfoDatum(profile);
1799 for (i=0; i < (ssize_t) nibbles; i++)
1801 while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1805 png_warning(ping, "ran out of profile data");
1806 profile=DestroyStringInfo(profile);
1807 return(MagickFalse);
1813 *dp=(unsigned char) (16*unhex[(int) *sp++]);
1816 (*dp++)+=unhex[(int) *sp++];
1819 We have already read "Raw profile type.
1821 (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1822 profile=DestroyStringInfo(profile);
1824 if (image_info->verbose)
1825 (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1830 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1831 #ifdef zxIf_SUPPORTED
1833 /* exif_inf() was derived from zlib-1.2.11/examples/zpipe.c/inf()
1834 Not copyrighted -- provided to the public domain
1835 Version 1.4 11 December 2005 Mark Adler */
1839 /* Decompress from source to dest (copied from zlib-1.2.11/examples).
1840 inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
1841 allocated for processing, Z_DATA_ERROR if the deflate data is
1842 invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
1843 the version of the library linked do not match, or Z_ERRNO if there
1844 is an error reading or writing the files. */
1846 int exif_inf(png_structp png_ptr, unsigned char *source,
1847 unsigned char **dest, size_t n, png_uint_32 inflated_size)
1849 /* *source: compressed data stream (input)
1850 *dest: inflated data (output)
1853 Returns one of the following:
1854 return(-1); chunk had an error
1855 return(n); success, n is length of inflated data
1861 size_t inflated_length = inflated_size;
1863 if (inflated_length >= PNG_USER_CHUNK_MALLOC_MAX - 1U ||
1864 inflated_length == 0)
1868 #if PNG_LIBPNG_VER >= 14000
1869 *dest=(unsigned char *) png_malloc(png_ptr,
1870 (png_alloc_size_t) inflated_length);
1872 *dest=(unsigned char *) png_malloc(png_ptr,
1873 (png_size_t) inflated_length);
1875 /* allocate inflate state */
1876 strm.zalloc = Z_NULL;
1877 strm.zfree = Z_NULL;
1878 strm.opaque = Z_NULL;
1880 strm.next_in = Z_NULL;
1881 ret = inflateInit(&strm);
1884 /* decompress until deflate stream ends or end of file */
1886 strm.avail_in = (int)n;
1887 strm.next_in = source;
1889 /* run inflate() on input until output buffer not full */
1891 strm.avail_out = (int)inflated_length;
1892 strm.next_out = *dest;
1893 ret = inflate(&strm, Z_NO_FLUSH);
1894 assert(ret != Z_STREAM_ERROR); /* state not clobbered */
1899 (void)inflateEnd(&strm);
1902 } while (strm.avail_out == 0);
1903 /* done when inflate() says it's done */
1904 } while (ret != Z_STREAM_END);
1906 /* clean up and return */
1908 /* To do: take care of too little or too much data */
1910 (void)inflateEnd(&strm);
1911 return (inflated_length);
1915 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1921 /* The unknown chunk structure contains the chunk data:
1926 Note that libpng has already taken care of the CRC handling.
1928 Returns one of the following:
1929 return(-n); chunk had an error
1930 return(0); did not recognize
1934 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1935 " read_user_chunk: found %c%c%c%c chunk",
1936 chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1938 #if defined(zxIf_SUPPORTED)
1939 if ((chunk->name[0] == 122 || chunk->name[0] == 117 ) &&
1940 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
1941 chunk->name[2] == 73 &&
1942 chunk-> name[3] == 102)
1944 /* process uxIf or zxIf chunk */
1960 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1961 " recognized uxIf|zxIf chunk");
1963 image=(Image *) png_get_user_chunk_ptr(ping);
1965 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1967 profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1969 if (profile == (StringInfo *) NULL)
1971 (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1972 ResourceLimitError,"MemoryAllocationFailed","`%s'",
1976 p=GetStringInfoDatum(profile);
1978 /* Initialize profile with "Exif\0\0" */
1986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1987 " initialized uxIf|zxIf chunk");
1989 switch (chunk->data[0])
1997 /* copy chunk->data to profile */
1999 for (i=0; i<chunk->size; i++)
2002 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2003 " SetImageProfile with %lu bytes",
2004 (unsigned long) chunk->size+6);
2005 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2006 (void) SetImageProfile(image,"exif",profile,
2007 error_info->exception);
2012 /* Zlib compressed */
2017 png_uint_32 inflated_size;
2019 png_free(ping,profile);
2021 if (chunk->size < 5)
2025 s++; // skip compression byte
2027 inflated_size = (png_uint_32)
2028 (((s[0] & 0xff) << 24) | ((s[1] & 0xff) << 16) |
2029 ((s[2] & 0xff) << 8) | ((s[3] & 0xff) ));
2033 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2034 " inflated_size = %lu bytes",inflated_size);
2036 /* uncompress chunk->data to temporary profile */
2037 inflated_size=exif_inf(ping,s,&temp,chunk->size-1,inflated_size);
2039 if (inflated_size <= 0)
2041 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2042 " inflated_size = %lu bytes",inflated_size);
2045 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2047 profile=BlobToStringInfo((const void *) NULL,inflated_size+6);
2049 if (profile == (StringInfo *) NULL)
2051 (void) ThrowMagickException(error_info->exception,
2053 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2057 p=GetStringInfoDatum(profile);
2058 /* Initialize profile with "Exif\0\0" */
2066 for (i=0; i<inflated_size; i++)
2069 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2070 " SetImageProfile with %lu bytes",
2071 (unsigned long) inflated_size+6);
2072 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2073 (void) SetImageProfile(image,"exif",profile,
2074 error_info->exception);
2076 png_free(ping,temp);
2082 #endif /* zxIf_SUPPORTED */
2084 if (chunk->name[0] == 101 &&
2085 (chunk->name[1] == 88 || chunk->name[1] == 120 ) &&
2086 chunk->name[2] == 73 &&
2087 chunk-> name[3] == 102)
2089 /* process eXIf or exIf chunk */
2106 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2107 " recognized eXIf|exIf chunk");
2109 image=(Image *) png_get_user_chunk_ptr(ping);
2111 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2113 profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
2115 if (profile == (StringInfo *) NULL)
2117 (void) ThrowMagickException(error_info->exception,GetMagickModule(),
2118 ResourceLimitError,"MemoryAllocationFailed","`%s'",
2122 p=GetStringInfoDatum(profile);
2124 /* Initialize profile with "Exif\0\0" */
2132 /* copy chunk->data to profile */
2134 for (i=0; i<chunk->size; i++)
2137 error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2138 (void) SetImageProfile(image,"exif",profile,
2139 error_info->exception);
2144 /* vpAg (deprecated, replaced by caNv) */
2145 if (chunk->name[0] == 118 &&
2146 chunk->name[1] == 112 &&
2147 chunk->name[2] == 65 &&
2148 chunk->name[3] == 103)
2150 /* recognized vpAg */
2152 if (chunk->size != 9)
2153 return(-1); /* Error return */
2155 if (chunk->data[8] != 0)
2156 return(0); /* ImageMagick requires pixel units */
2158 image=(Image *) png_get_user_chunk_ptr(ping);
2160 image->page.width=(size_t) ((chunk->data[0] << 24) |
2161 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2163 image->page.height=(size_t) ((chunk->data[4] << 24) |
2164 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2170 if (chunk->name[0] == 99 &&
2171 chunk->name[1] == 97 &&
2172 chunk->name[2] == 78 &&
2173 chunk->name[3] == 118)
2175 /* recognized caNv */
2177 if (chunk->size != 16)
2178 return(-1); /* Error return */
2180 image=(Image *) png_get_user_chunk_ptr(ping);
2182 image->page.width=(size_t) ((chunk->data[0] << 24) |
2183 (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
2185 image->page.height=(size_t) ((chunk->data[4] << 24) |
2186 (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
2188 image->page.x=(size_t) ((chunk->data[8] << 24) |
2189 (chunk->data[9] << 16) | (chunk->data[10] << 8) | chunk->data[11]);
2191 image->page.y=(size_t) ((chunk->data[12] << 24) |
2192 (chunk->data[13] << 16) | (chunk->data[14] << 8) | chunk->data[15]);
2197 return(0); /* Did not recognize */
2199 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2201 #if defined(PNG_tIME_SUPPORTED)
2202 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2203 ExceptionInfo *exception)
2208 if (png_get_tIME(ping,info,&time))
2213 FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2214 time->year,time->month,time->day,time->hour,time->minute,time->second);
2215 SetImageProperty(image,"png:tIME",timestamp,exception);
2221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2225 % R e a d O n e P N G I m a g e %
2229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2231 % ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2232 % (minus the 8-byte signature) and returns it. It allocates the memory
2233 % necessary for the new Image structure and returns a pointer to the new
2236 % The format of the ReadOnePNGImage method is:
2238 % Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2239 % ExceptionInfo *exception)
2241 % A description of each parameter follows:
2243 % o mng_info: Specifies a pointer to a MngInfo structure.
2245 % o image_info: the image info.
2247 % o exception: return any errors or warnings in this structure.
2250 static Image *ReadOnePNGImage(MngInfo *mng_info,
2251 const ImageInfo *image_info, ExceptionInfo *exception)
2253 /* Read one PNG image */
2255 /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2268 intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2278 ping_interlace_method,
2279 ping_compression_method,
2293 ping_found_sRGB_cHRM,
2298 *volatile pixel_info;
2336 register unsigned char
2356 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2357 png_byte unused_chunks[]=
2359 104, 73, 83, 84, (png_byte) '\0', /* hIST */
2360 105, 84, 88, 116, (png_byte) '\0', /* iTXt */
2361 112, 67, 65, 76, (png_byte) '\0', /* pCAL */
2362 115, 67, 65, 76, (png_byte) '\0', /* sCAL */
2363 115, 80, 76, 84, (png_byte) '\0', /* sPLT */
2364 #if !defined(PNG_tIME_SUPPORTED)
2365 116, 73, 77, 69, (png_byte) '\0', /* tIME */
2367 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2368 /* ignore the APNG chunks */
2369 97, 99, 84, 76, (png_byte) '\0', /* acTL */
2370 102, 99, 84, 76, (png_byte) '\0', /* fcTL */
2371 102, 100, 65, 84, (png_byte) '\0', /* fdAT */
2376 /* Define these outside of the following "if logging()" block so they will
2377 * show in debuggers.
2380 (void) ConcatenateMagickString(im_vers,
2381 MagickLibVersionText,32);
2382 (void) ConcatenateMagickString(im_vers,
2383 MagickLibAddendum,32);
2386 (void) ConcatenateMagickString(libpng_vers,
2387 PNG_LIBPNG_VER_STRING,32);
2389 (void) ConcatenateMagickString(libpng_runv,
2390 png_get_libpng_ver(NULL),32);
2393 (void) ConcatenateMagickString(zlib_vers,
2396 (void) ConcatenateMagickString(zlib_runv,
2399 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2400 " Enter ReadOnePNGImage()\n"
2401 " IM version = %s\n"
2402 " Libpng version = %s",
2403 im_vers, libpng_vers);
2405 if (logging != MagickFalse)
2407 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2409 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2412 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
2414 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2416 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
2421 #if (PNG_LIBPNG_VER < 10200)
2422 if (image_info->verbose)
2423 printf("Your PNG library (libpng-%s) is rather old.\n",
2424 PNG_LIBPNG_VER_STRING);
2427 #if (PNG_LIBPNG_VER >= 10400)
2428 # ifndef PNG_TRANSFORM_GRAY_TO_RGB /* Added at libpng-1.4.0beta67 */
2429 if (image_info->verbose)
2431 printf("Your PNG library (libpng-%s) is an old beta version.\n",
2432 PNG_LIBPNG_VER_STRING);
2433 printf("Please update it.\n");
2439 quantum_info = (QuantumInfo *) NULL;
2440 image=mng_info->image;
2442 if (logging != MagickFalse)
2444 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2445 " Before reading:\n"
2446 " image->alpha_trait=%d"
2447 " image->rendering_intent=%d\n"
2448 " image->colorspace=%d\n"
2450 (int) image->alpha_trait, (int) image->rendering_intent,
2451 (int) image->colorspace, image->gamma);
2454 Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2456 /* Set to an out-of-range color unless tRNS chunk is present */
2457 transparent_color.red=65537;
2458 transparent_color.green=65537;
2459 transparent_color.blue=65537;
2460 transparent_color.alpha=65537;
2465 num_raw_profiles = 0;
2467 ping_found_cHRM = MagickFalse;
2468 ping_found_gAMA = MagickFalse;
2469 ping_found_iCCP = MagickFalse;
2470 ping_found_sRGB = MagickFalse;
2471 ping_found_sRGB_cHRM = MagickFalse;
2472 ping_preserve_iCCP = MagickFalse;
2476 Allocate the PNG structures
2478 #ifdef PNG_USER_MEM_SUPPORTED
2479 error_info.image=image;
2480 error_info.exception=exception;
2481 ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2482 MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2483 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2485 ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2486 MagickPNGErrorHandler,MagickPNGWarningHandler);
2488 if (ping == (png_struct *) NULL)
2489 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2491 ping_info=png_create_info_struct(ping);
2493 if (ping_info == (png_info *) NULL)
2495 png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2496 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2499 end_info=png_create_info_struct(ping);
2501 if (end_info == (png_info *) NULL)
2503 png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2504 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2507 pixel_info=(MemoryInfo *) NULL;
2509 if (setjmp(png_jmpbuf(ping)))
2512 PNG image is corrupt.
2514 png_destroy_read_struct(&ping,&ping_info,&end_info);
2516 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2517 UnlockSemaphoreInfo(ping_semaphore);
2520 if (pixel_info != (MemoryInfo *) NULL)
2521 pixel_info=RelinquishVirtualMemory(pixel_info);
2523 if (logging != MagickFalse)
2524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2525 " exit ReadOnePNGImage() with error.");
2527 return(GetFirstImageInList(image));
2530 /* { For navigation to end of SETJMP-protected block. Within this
2531 * block, use png_error() instead of Throwing an Exception, to ensure
2532 * that libpng is able to clean up, and that the semaphore is unlocked.
2535 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2536 LockSemaphoreInfo(ping_semaphore);
2539 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2540 /* Allow benign errors */
2541 png_set_benign_errors(ping, 1);
2544 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2545 /* Reject images with too many rows or columns */
2546 png_set_user_limits(ping,
2547 (png_uint_32) MagickMin(0x7fffffffL,
2548 GetMagickResourceLimit(WidthResource)),
2549 (png_uint_32) MagickMin(0x7fffffffL,
2550 GetMagickResourceLimit(HeightResource)));
2551 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2554 Prepare PNG for reading.
2557 mng_info->image_found++;
2558 png_set_sig_bytes(ping,8);
2560 if (LocaleCompare(image_info->magick,"MNG") == 0)
2562 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2563 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2564 png_set_read_fn(ping,image,png_get_data);
2566 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2567 png_permit_empty_plte(ping,MagickTrue);
2568 png_set_read_fn(ping,image,png_get_data);
2570 mng_info->image=image;
2571 mng_info->bytes_in_read_buffer=0;
2572 mng_info->found_empty_plte=MagickFalse;
2573 mng_info->have_saved_bkgd_index=MagickFalse;
2574 png_set_read_fn(ping,mng_info,mng_get_data);
2580 png_set_read_fn(ping,image,png_get_data);
2586 value=GetImageOption(image_info,"profile:skip");
2588 if (IsOptionMember("ICC",value) == MagickFalse)
2591 value=GetImageOption(image_info,"png:preserve-iCCP");
2594 value=GetImageArtifact(image,"png:preserve-iCCP");
2597 ping_preserve_iCCP=MagickTrue;
2599 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2600 /* Don't let libpng check for ICC/sRGB profile because we're going
2601 * to do that anyway. This feature was added at libpng-1.6.12.
2602 * If logging, go ahead and check and issue a warning as appropriate.
2604 if (logging == MagickFalse)
2605 png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2608 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2611 png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2615 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2616 /* Ignore unused chunks and all unknown chunks except for eXIf,
2617 zxIf, caNv, and vpAg */
2618 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2619 png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2621 png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2623 #if defined(zxIf_SUPPORTED)
2624 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_uxIf, 1);
2625 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_zxIf, 1);
2627 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_eXIf, 1);
2628 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2629 png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2630 png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2631 (int)sizeof(unused_chunks)/5);
2632 /* Callback for other unknown chunks */
2633 png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2636 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2637 # if (PNG_LIBPNG_VER >= 10400)
2638 /* Limit the size of the chunk storage cache used for sPLT, text,
2639 * and unknown chunks.
2641 png_set_chunk_cache_max(ping, 32767);
2645 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2646 /* Disable new libpng-1.5.10 feature */
2647 png_set_check_for_invalid_index (ping, 0);
2650 #if (PNG_LIBPNG_VER < 10400)
2651 # if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2652 (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2653 /* Disable thread-unsafe features of pnggccrd */
2654 if (png_access_version_number() >= 10200)
2656 png_uint_32 mmx_disable_mask=0;
2657 png_uint_32 asm_flags;
2659 mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \
2660 | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \
2661 | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \
2662 | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2663 asm_flags=png_get_asm_flags(ping);
2664 png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2669 png_read_info(ping,ping_info);
2671 png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2672 &ping_bit_depth,&ping_color_type,
2673 &ping_interlace_method,&ping_compression_method,
2674 &ping_filter_method);
2676 ping_file_depth = ping_bit_depth;
2678 /* Swap bytes if requested */
2679 if (ping_file_depth == 16)
2684 value=GetImageOption(image_info,"png:swap-bytes");
2687 value=GetImageArtifact(image,"png:swap-bytes");
2693 /* Save bit-depth and color-type in case we later want to write a PNG00 */
2696 msg[MagickPathExtent];
2698 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2699 (int) ping_color_type);
2700 (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2702 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2703 (int) ping_bit_depth);
2704 (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2707 (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2710 (void) png_get_bKGD(ping, ping_info, &ping_background);
2712 if (ping_bit_depth < 8)
2714 png_set_packing(ping);
2718 image->depth=ping_bit_depth;
2719 image->depth=GetImageQuantumDepth(image,MagickFalse);
2720 image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2722 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2723 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2725 image->rendering_intent=UndefinedIntent;
2726 intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2727 (void) ResetMagickMemory(&image->chromaticity,0,
2728 sizeof(image->chromaticity));
2731 if (logging != MagickFalse)
2733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734 " PNG width: %.20g, height: %.20g\n"
2735 " PNG color_type: %d, bit_depth: %d\n"
2736 " PNG compression_method: %d\n"
2737 " PNG interlace_method: %d, filter_method: %d",
2738 (double) ping_width, (double) ping_height,
2739 ping_color_type, ping_bit_depth,
2740 ping_compression_method,
2741 ping_interlace_method,ping_filter_method);
2745 if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2747 ping_found_iCCP=MagickTrue;
2748 if (logging != MagickFalse)
2749 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2750 " Found PNG iCCP chunk.");
2753 if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2755 ping_found_gAMA=MagickTrue;
2756 if (logging != MagickFalse)
2757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2758 " Found PNG gAMA chunk.");
2761 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2763 ping_found_cHRM=MagickTrue;
2764 if (logging != MagickFalse)
2765 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2766 " Found PNG cHRM chunk.");
2769 if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2772 ping_found_sRGB=MagickTrue;
2773 if (logging != MagickFalse)
2774 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2775 " Found PNG sRGB chunk.");
2778 #ifdef PNG_READ_iCCP_SUPPORTED
2779 if (ping_found_iCCP !=MagickTrue &&
2780 ping_found_sRGB != MagickTrue &&
2781 png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2783 ping_found_iCCP=MagickTrue;
2784 if (logging != MagickFalse)
2785 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2786 " Found PNG iCCP chunk.");
2789 if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2794 #if (PNG_LIBPNG_VER < 10500)
2808 (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2811 if (profile_length != 0)
2816 if (logging != MagickFalse)
2817 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2818 " Reading PNG iCCP chunk.");
2820 profile=BlobToStringInfo(info,profile_length);
2822 if (profile == (StringInfo *) NULL)
2824 png_warning(ping, "ICC profile is NULL");
2825 profile=DestroyStringInfo(profile);
2829 if (ping_preserve_iCCP == MagickFalse)
2843 length=(png_uint_32) GetStringInfoLength(profile);
2845 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2847 if (length == sRGB_info[icheck].len)
2851 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2852 " Got a %lu-byte ICC profile (potentially sRGB)",
2853 (unsigned long) length);
2855 data=GetStringInfoDatum(profile);
2856 profile_crc=crc32(0,data,length);
2858 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2859 " with crc=%8x",(unsigned int) profile_crc);
2863 if (profile_crc == sRGB_info[icheck].crc)
2865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2866 " It is sRGB with rendering intent = %s",
2867 Magick_RenderingIntentString_from_PNG_RenderingIntent(
2868 sRGB_info[icheck].intent));
2869 if (image->rendering_intent==UndefinedIntent)
2871 image->rendering_intent=
2872 Magick_RenderingIntent_from_PNG_RenderingIntent(
2873 sRGB_info[icheck].intent);
2879 if (sRGB_info[icheck].len == 0)
2881 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2882 " Got %lu-byte ICC profile not recognized as sRGB",
2883 (unsigned long) length);
2884 (void) SetImageProfile(image,"icc",profile,exception);
2887 else /* Preserve-iCCP */
2889 (void) SetImageProfile(image,"icc",profile,exception);
2892 profile=DestroyStringInfo(profile);
2898 #if defined(PNG_READ_sRGB_SUPPORTED)
2900 if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2903 if (png_get_sRGB(ping,ping_info,&intent))
2905 if (image->rendering_intent == UndefinedIntent)
2906 image->rendering_intent=
2907 Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2909 if (logging != MagickFalse)
2910 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2911 " Reading PNG sRGB chunk: rendering_intent: %d",intent);
2915 else if (mng_info->have_global_srgb)
2917 if (image->rendering_intent == UndefinedIntent)
2918 image->rendering_intent=
2919 Magick_RenderingIntent_from_PNG_RenderingIntent
2920 (mng_info->global_srgb_intent);
2927 if (!png_get_gAMA(ping,ping_info,&file_gamma))
2928 if (mng_info->have_global_gama)
2929 png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2931 if (png_get_gAMA(ping,ping_info,&file_gamma))
2933 image->gamma=(float) file_gamma;
2934 if (logging != MagickFalse)
2935 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2936 " Reading PNG gAMA chunk: gamma: %f",file_gamma);
2940 if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2942 if (mng_info->have_global_chrm != MagickFalse)
2944 (void) png_set_cHRM(ping,ping_info,
2945 mng_info->global_chrm.white_point.x,
2946 mng_info->global_chrm.white_point.y,
2947 mng_info->global_chrm.red_primary.x,
2948 mng_info->global_chrm.red_primary.y,
2949 mng_info->global_chrm.green_primary.x,
2950 mng_info->global_chrm.green_primary.y,
2951 mng_info->global_chrm.blue_primary.x,
2952 mng_info->global_chrm.blue_primary.y);
2956 if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2958 (void) png_get_cHRM(ping,ping_info,
2959 &image->chromaticity.white_point.x,
2960 &image->chromaticity.white_point.y,
2961 &image->chromaticity.red_primary.x,
2962 &image->chromaticity.red_primary.y,
2963 &image->chromaticity.green_primary.x,
2964 &image->chromaticity.green_primary.y,
2965 &image->chromaticity.blue_primary.x,
2966 &image->chromaticity.blue_primary.y);
2968 ping_found_cHRM=MagickTrue;
2970 if (image->chromaticity.red_primary.x>0.6399f &&
2971 image->chromaticity.red_primary.x<0.6401f &&
2972 image->chromaticity.red_primary.y>0.3299f &&
2973 image->chromaticity.red_primary.y<0.3301f &&
2974 image->chromaticity.green_primary.x>0.2999f &&
2975 image->chromaticity.green_primary.x<0.3001f &&
2976 image->chromaticity.green_primary.y>0.5999f &&
2977 image->chromaticity.green_primary.y<0.6001f &&
2978 image->chromaticity.blue_primary.x>0.1499f &&
2979 image->chromaticity.blue_primary.x<0.1501f &&
2980 image->chromaticity.blue_primary.y>0.0599f &&
2981 image->chromaticity.blue_primary.y<0.0601f &&
2982 image->chromaticity.white_point.x>0.3126f &&
2983 image->chromaticity.white_point.x<0.3128f &&
2984 image->chromaticity.white_point.y>0.3289f &&
2985 image->chromaticity.white_point.y<0.3291f)
2986 ping_found_sRGB_cHRM=MagickTrue;
2989 if (image->rendering_intent != UndefinedIntent)
2991 if (ping_found_sRGB != MagickTrue &&
2992 (ping_found_gAMA != MagickTrue ||
2993 (image->gamma > .45 && image->gamma < .46)) &&
2994 (ping_found_cHRM != MagickTrue ||
2995 ping_found_sRGB_cHRM != MagickFalse) &&
2996 ping_found_iCCP != MagickTrue)
2998 png_set_sRGB(ping,ping_info,
2999 Magick_RenderingIntent_to_PNG_RenderingIntent
3000 (image->rendering_intent));
3001 file_gamma=1.000f/2.200f;
3002 ping_found_sRGB=MagickTrue;
3003 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3004 " Setting sRGB as if in input");
3008 #if defined(PNG_oFFs_SUPPORTED)
3009 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3011 image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
3012 image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
3014 if (logging != MagickFalse)
3015 if (image->page.x || image->page.y)
3016 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3017 " Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
3018 image->page.x,(double) image->page.y);
3021 #if defined(PNG_pHYs_SUPPORTED)
3022 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3024 if (mng_info->have_global_phys)
3026 png_set_pHYs(ping,ping_info,
3027 mng_info->global_x_pixels_per_unit,
3028 mng_info->global_y_pixels_per_unit,
3029 mng_info->global_phys_unit_type);
3036 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3039 Set image resolution.
3041 (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
3043 image->resolution.x=(double) x_resolution;
3044 image->resolution.y=(double) y_resolution;
3046 if (unit_type == PNG_RESOLUTION_METER)
3048 image->units=PixelsPerCentimeterResolution;
3049 image->resolution.x=(double) x_resolution/100.0;
3050 image->resolution.y=(double) y_resolution/100.0;
3053 if (logging != MagickFalse)
3054 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3055 " Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
3056 (double) x_resolution,(double) y_resolution,unit_type);
3060 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3065 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3067 if ((number_colors == 0) &&
3068 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
3070 if (mng_info->global_plte_length)
3072 png_set_PLTE(ping,ping_info,mng_info->global_plte,
3073 (int) mng_info->global_plte_length);
3075 if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3077 if (mng_info->global_trns_length)
3080 "global tRNS has more entries than global PLTE");
3084 png_set_tRNS(ping,ping_info,mng_info->global_trns,
3085 (int) mng_info->global_trns_length,NULL);
3088 #ifdef PNG_READ_bKGD_SUPPORTED
3090 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3091 mng_info->have_saved_bkgd_index ||
3093 png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3098 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3099 if (mng_info->have_saved_bkgd_index)
3100 background.index=mng_info->saved_bkgd_index;
3102 if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3103 background.index=ping_background->index;
3105 background.red=(png_uint_16)
3106 mng_info->global_plte[background.index].red;
3108 background.green=(png_uint_16)
3109 mng_info->global_plte[background.index].green;
3111 background.blue=(png_uint_16)
3112 mng_info->global_plte[background.index].blue;
3114 background.gray=(png_uint_16)
3115 mng_info->global_plte[background.index].green;
3117 png_set_bKGD(ping,ping_info,&background);
3122 png_error(ping,"No global PLTE in file");
3126 #ifdef PNG_READ_bKGD_SUPPORTED
3127 if (mng_info->have_global_bkgd &&
3128 (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3129 image->background_color=mng_info->mng_global_bkgd;
3131 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3136 /* Set image background color.
3137 * Scale background components to 16-bit, then scale
3143 if (ping_file_depth == 1)
3146 else if (ping_file_depth == 2)
3149 else if (ping_file_depth == 4)
3152 if (ping_file_depth <= 8)
3155 ping_background->red *= bkgd_scale;
3156 ping_background->green *= bkgd_scale;
3157 ping_background->blue *= bkgd_scale;
3159 if (logging != MagickFalse)
3161 if (logging != MagickFalse)
3162 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3163 " Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3164 " bkgd_scale=%d. ping_background=(%d,%d,%d)",
3165 ping_background->red,ping_background->green,
3166 ping_background->blue,
3167 bkgd_scale,ping_background->red,
3168 ping_background->green,ping_background->blue);
3171 image->background_color.red=
3172 ScaleShortToQuantum(ping_background->red);
3174 image->background_color.green=
3175 ScaleShortToQuantum(ping_background->green);
3177 image->background_color.blue=
3178 ScaleShortToQuantum(ping_background->blue);
3180 image->background_color.alpha=OpaqueAlpha;
3182 if (logging != MagickFalse)
3183 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3184 " image->background_color=(%.20g,%.20g,%.20g).",
3185 (double) image->background_color.red,
3186 (double) image->background_color.green,
3187 (double) image->background_color.blue);
3189 #endif /* PNG_READ_bKGD_SUPPORTED */
3191 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3194 Image has a tRNS chunk.
3202 if (logging != MagickFalse)
3203 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3204 " Reading PNG tRNS chunk.");
3206 max_sample = (int) ((one << ping_file_depth) - 1);
3208 if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3209 (int)ping_trans_color->gray > max_sample) ||
3210 (ping_color_type == PNG_COLOR_TYPE_RGB &&
3211 ((int)ping_trans_color->red > max_sample ||
3212 (int)ping_trans_color->green > max_sample ||
3213 (int)ping_trans_color->blue > max_sample)))
3215 if (logging != MagickFalse)
3216 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3217 " Ignoring PNG tRNS chunk with out-of-range sample.");
3218 png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3219 png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3220 image->alpha_trait=UndefinedPixelTrait;
3227 scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3229 /* Scale transparent_color to short */
3230 transparent_color.red= scale_to_short*ping_trans_color->red;
3231 transparent_color.green= scale_to_short*ping_trans_color->green;
3232 transparent_color.blue= scale_to_short*ping_trans_color->blue;
3233 transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3235 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3237 if (logging != MagickFalse)
3239 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3240 " Raw tRNS graylevel = %d, scaled graylevel = %d.",
3241 (int) ping_trans_color->gray,(int) transparent_color.alpha);
3244 transparent_color.red=transparent_color.alpha;
3245 transparent_color.green=transparent_color.alpha;
3246 transparent_color.blue=transparent_color.alpha;
3250 #if defined(PNG_READ_sBIT_SUPPORTED)
3251 if (mng_info->have_global_sbit)
3253 if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3254 png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3257 num_passes=png_set_interlace_handling(ping);
3259 png_read_update_info(ping,ping_info);
3261 ping_rowbytes=png_get_rowbytes(ping,ping_info);
3264 Initialize image structure.
3266 mng_info->image_box.left=0;
3267 mng_info->image_box.right=(ssize_t) ping_width;
3268 mng_info->image_box.top=0;
3269 mng_info->image_box.bottom=(ssize_t) ping_height;
3270 if (mng_info->mng_type == 0)
3272 mng_info->mng_width=ping_width;
3273 mng_info->mng_height=ping_height;
3274 mng_info->frame=mng_info->image_box;
3275 mng_info->clip=mng_info->image_box;
3280 image->page.y=mng_info->y_off[mng_info->object_id];
3283 image->compression=ZipCompression;
3284 image->columns=ping_width;
3285 image->rows=ping_height;
3287 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3288 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3291 image_gamma = image->gamma;
3293 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3294 " image->gamma=%f",(float) image_gamma);
3296 if (image_gamma > 0.75)
3298 /* Set image->rendering_intent to Undefined,
3299 * image->colorspace to GRAY, and reset image->chromaticity.
3301 image->intensity = Rec709LuminancePixelIntensityMethod;
3302 SetImageColorspace(image,GRAYColorspace,exception);
3307 save_rendering_intent = image->rendering_intent;
3309 save_chromaticity = image->chromaticity;
3311 SetImageColorspace(image,GRAYColorspace,exception);
3312 image->rendering_intent = save_rendering_intent;
3313 image->chromaticity = save_chromaticity;
3316 image->gamma = image_gamma;
3319 (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3320 " image->colorspace=%d",(int) image->colorspace);
3322 if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3323 ((int) ping_bit_depth < 16 &&
3324 (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3329 image->storage_class=PseudoClass;
3331 image->colors=one << ping_file_depth;
3332 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3333 if (image->colors > 256)
3336 if (image->colors > 65536L)
3337 image->colors=65536L;
3339 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3344 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3345 image->colors=(size_t) number_colors;
3347 if (logging != MagickFalse)
3348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3349 " Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3353 if (image->storage_class == PseudoClass)
3356 Initialize image colormap.
3358 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3359 png_error(ping,"Memory allocation failed");
3361 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3366 (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3368 for (i=0; i < (ssize_t) number_colors; i++)
3370 image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3371 image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3372 image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3375 for ( ; i < (ssize_t) image->colors; i++)
3377 image->colormap[i].red=0;
3378 image->colormap[i].green=0;
3379 image->colormap[i].blue=0;
3388 scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3390 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3391 scale = ScaleShortToQuantum(scale);
3394 for (i=0; i < (ssize_t) image->colors; i++)
3396 image->colormap[i].red=(Quantum) (i*scale);
3397 image->colormap[i].green=(Quantum) (i*scale);
3398 image->colormap[i].blue=(Quantum) (i*scale);
3403 /* Set some properties for reporting by "identify" */
3406 msg[MagickPathExtent];
3408 /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3409 ping_interlace_method in value */
3411 (void) FormatLocaleString(msg,MagickPathExtent,
3412 "%d, %d",(int) ping_width, (int) ping_height);
3413 (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3415 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3416 (int) ping_file_depth);
3417 (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3419 (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3420 (int) ping_color_type,
3421 Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3422 (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3424 if (ping_interlace_method == 0)
3426 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3427 (int) ping_interlace_method);
3429 else if (ping_interlace_method == 1)
3431 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3432 (int) ping_interlace_method);
3436 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3437 (int) ping_interlace_method);
3439 (void) SetImageProperty(image,"png:IHDR.interlace_method",
3442 if (number_colors != 0)
3444 (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3445 (int) number_colors);
3446 (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3450 #if defined(PNG_tIME_SUPPORTED)
3451 read_tIME_chunk(image,ping,ping_info,exception);
3456 Read image scanlines.
3458 if (image->delay != 0)
3459 mng_info->scenes_found++;
3461 if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3462 (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3463 (image_info->first_scene+image_info->number_scenes))))
3465 /* This happens later in non-ping decodes */
3466 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3467 image->storage_class=DirectClass;
3469 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3470 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3471 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3472 BlendPixelTrait : UndefinedPixelTrait;
3474 if (logging != MagickFalse)
3475 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3476 " Skipping PNG image data for scene %.20g",(double)
3477 mng_info->scenes_found-1);
3478 png_destroy_read_struct(&ping,&ping_info,&end_info);
3480 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3481 UnlockSemaphoreInfo(ping_semaphore);
3484 if (logging != MagickFalse)
3485 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3486 " exit ReadOnePNGImage().");
3491 if (logging != MagickFalse)
3492 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3493 " Reading PNG IDAT chunk(s)");
3495 status=SetImageExtent(image,image->columns,image->rows,exception);
3496 if (status == MagickFalse)
3497 return(DestroyImageList(image));
3500 pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3501 sizeof(*ping_pixels));
3503 pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3505 if (pixel_info == (MemoryInfo *) NULL)
3506 png_error(ping,"Memory allocation failed");
3507 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3509 if (logging != MagickFalse)
3510 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3511 " Converting PNG pixels to pixel packets");
3513 Convert PNG pixels to pixel packets.
3515 quantum_info=AcquireQuantumInfo(image_info,image);
3517 if (quantum_info == (QuantumInfo *) NULL)
3518 png_error(ping,"Failed to allocate quantum_info");
3520 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3525 found_transparent_pixel;
3527 found_transparent_pixel=MagickFalse;
3529 if (image->storage_class == DirectClass)
3531 for (pass=0; pass < num_passes; pass++)
3534 Convert image to DirectClass pixel packets.
3537 (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3538 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3539 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3540 BlendPixelTrait : UndefinedPixelTrait;
3542 for (y=0; y < (ssize_t) image->rows; y++)
3545 row_offset=ping_rowbytes*y;
3550 png_read_row(ping,ping_pixels+row_offset,NULL);
3552 if (pass < num_passes-1)
3555 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3557 if (q == (Quantum *) NULL)
3560 if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3561 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3562 GrayQuantum,ping_pixels+row_offset,exception);
3564 else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3565 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3566 GrayAlphaQuantum,ping_pixels+row_offset,exception);
3568 else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3569 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3570 RGBAQuantum,ping_pixels+row_offset,exception);
3572 else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3573 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3574 IndexQuantum,ping_pixels+row_offset,exception);
3576 else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3577 (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3578 RGBQuantum,ping_pixels+row_offset,exception);
3580 if (found_transparent_pixel == MagickFalse)
3582 /* Is there a transparent pixel in the row? */
3583 if (y== 0 && logging != MagickFalse)
3584 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3585 " Looking for cheap transparent pixel");
3587 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3589 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3590 ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3591 (GetPixelAlpha(image,q) != OpaqueAlpha))
3593 if (logging != MagickFalse)
3594 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3597 found_transparent_pixel = MagickTrue;
3600 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3601 ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3602 (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3603 transparent_color.red &&
3604 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3605 transparent_color.green &&
3606 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3607 transparent_color.blue))
3609 if (logging != MagickFalse)
3610 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3612 found_transparent_pixel = MagickTrue;
3615 q+=GetPixelChannels(image);
3619 if (num_passes == 1)
3621 status=SetImageProgress(image,LoadImageTag,
3622 (MagickOffsetType) y, image->rows);
3624 if (status == MagickFalse)
3627 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3631 if (num_passes != 1)
3633 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3634 if (status == MagickFalse)
3640 else /* image->storage_class != DirectClass */
3642 for (pass=0; pass < num_passes; pass++)
3651 Convert grayscale image to PseudoClass pixel packets.
3653 if (logging != MagickFalse)
3654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3655 " Converting grayscale pixels to pixel packets");
3657 image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3658 BlendPixelTrait : UndefinedPixelTrait;
3660 quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3661 (image->alpha_trait == BlendPixelTrait? 2 : 1)*
3662 sizeof(*quantum_scanline));
3664 if (quantum_scanline == (Quantum *) NULL)
3665 png_error(ping,"Memory allocation failed");
3667 for (y=0; y < (ssize_t) image->rows; y++)
3673 row_offset=ping_rowbytes*y;
3678 png_read_row(ping,ping_pixels+row_offset,NULL);
3680 if (pass < num_passes-1)
3683 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3685 if (q == (Quantum *) NULL)
3688 p=ping_pixels+row_offset;
3691 switch (ping_bit_depth)
3696 if (ping_color_type == 4)
3697 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3701 alpha=ScaleCharToQuantum((unsigned char)*p++);
3703 SetPixelAlpha(image,alpha,q);
3705 if (alpha != OpaqueAlpha)
3706 found_transparent_pixel = MagickTrue;
3708 q+=GetPixelChannels(image);
3712 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3720 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3722 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3726 if (image->colors > 256)
3727 quantum=((*p++) << 8);
3733 *r=ScaleShortToQuantum(quantum);
3736 if (ping_color_type == 4)
3738 if (image->colors > 256)
3739 quantum=((*p++) << 8);
3745 alpha=ScaleShortToQuantum(quantum);
3746 SetPixelAlpha(image,alpha,q);
3748 if (alpha != OpaqueAlpha)
3749 found_transparent_pixel = MagickTrue;
3751 q+=GetPixelChannels(image);
3754 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3756 p++; /* strip low byte */
3758 if (ping_color_type == 4)
3760 SetPixelAlpha(image,*p++,q);
3762 if (GetPixelAlpha(image,q) != OpaqueAlpha)
3763 found_transparent_pixel = MagickTrue;
3766 q+=GetPixelChannels(image);
3779 Transfer image scanline.
3783 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3785 if (q == (Quantum *) NULL)
3787 for (x=0; x < (ssize_t) image->columns; x++)
3789 SetPixelIndex(image,*r++,q);
3790 q+=GetPixelChannels(image);
3793 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3796 if (num_passes == 1)
3798 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3801 if (status == MagickFalse)
3806 if (num_passes != 1)
3808 status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3810 if (status == MagickFalse)
3814 quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3817 image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3818 UndefinedPixelTrait;
3820 if (logging != MagickFalse)
3822 if (found_transparent_pixel != MagickFalse)
3823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3824 " Found transparent pixel");
3827 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3828 " No transparent pixel was found");
3830 ping_color_type&=0x03;
3835 quantum_info=DestroyQuantumInfo(quantum_info);
3837 if (image->storage_class == PseudoClass)
3842 alpha_trait=image->alpha_trait;
3843 image->alpha_trait=UndefinedPixelTrait;
3844 (void) SyncImage(image,exception);
3845 image->alpha_trait=alpha_trait;
3848 png_read_end(ping,end_info);
3850 if (logging != MagickFalse)
3852 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3853 " image->storage_class=%d\n",(int) image->storage_class);
3856 if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3857 (ssize_t) image_info->first_scene && image->delay != 0)
3859 png_destroy_read_struct(&ping,&ping_info,&end_info);
3860 pixel_info=RelinquishVirtualMemory(pixel_info);
3862 (void) SetImageBackgroundColor(image,exception);
3863 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3864 UnlockSemaphoreInfo(ping_semaphore);
3866 if (logging != MagickFalse)
3867 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3868 " exit ReadOnePNGImage() early.");
3872 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3878 Image has a transparent background.
3880 storage_class=image->storage_class;
3881 image->alpha_trait=BlendPixelTrait;
3883 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3885 if (storage_class == PseudoClass)
3887 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3889 for (x=0; x < ping_num_trans; x++)
3891 image->colormap[x].alpha_trait=BlendPixelTrait;
3892 image->colormap[x].alpha =
3893 ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3897 else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3899 for (x=0; x < (int) image->colors; x++)
3901 if (ScaleQuantumToShort(image->colormap[x].red) ==
3902 transparent_color.alpha)
3904 image->colormap[x].alpha_trait=BlendPixelTrait;
3905 image->colormap[x].alpha = (Quantum) TransparentAlpha;
3909 (void) SyncImage(image,exception);
3912 #if 1 /* Should have already been done above, but glennrp problem P10
3917 for (y=0; y < (ssize_t) image->rows; y++)
3919 image->storage_class=storage_class;
3920 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3922 if (q == (Quantum *) NULL)
3926 /* Caution: on a Q8 build, this does not distinguish between
3927 * 16-bit colors that differ only in the low byte
3929 for (x=(ssize_t) image->columns-1; x >= 0; x--)
3931 if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3932 transparent_color.red &&
3933 ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3934 transparent_color.green &&
3935 ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3936 transparent_color.blue)
3938 SetPixelAlpha(image,TransparentAlpha,q);
3941 #if 0 /* I have not found a case where this is needed. */
3944 SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3948 q+=GetPixelChannels(image);
3951 if (SyncAuthenticPixels(image,exception) == MagickFalse)
3957 image->storage_class=DirectClass;
3960 for (j = 0; j < 2; j++)
3963 status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3964 MagickTrue : MagickFalse;
3966 status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3967 MagickTrue : MagickFalse;
3969 if (status != MagickFalse)
3970 for (i=0; i < (ssize_t) num_text; i++)
3972 /* Check for a profile */
3974 if (logging != MagickFalse)
3975 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3976 " Reading PNG text chunk");
3978 if (strlen(text[i].key) > 16 &&
3979 memcmp(text[i].key, "Raw profile type ",17) == 0)
3984 value=GetImageOption(image_info,"profile:skip");
3986 if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3988 (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3991 if (logging != MagickFalse)
3992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3993 " Read raw profile %s",text[i].key+17);
3997 if (logging != MagickFalse)
3998 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3999 " Skipping raw profile %s",text[i].key+17);
4008 length=text[i].text_length;
4009 value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
4011 if (value == (char *) NULL)
4013 png_error(ping,"Memory allocation failed");
4017 (void) ConcatenateMagickString(value,text[i].text,length+2);
4019 /* Don't save "density" or "units" property if we have a pHYs
4022 if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
4023 (LocaleCompare(text[i].key,"density") != 0 &&
4024 LocaleCompare(text[i].key,"units") != 0))
4025 (void) SetImageProperty(image,text[i].key,value,exception);
4027 if (logging != MagickFalse)
4029 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4032 (unsigned long) length,
4036 value=DestroyString(value);
4039 num_text_total += num_text;
4042 #ifdef MNG_OBJECT_BUFFERS
4044 Store the object if necessary.
4046 if (object_id && !mng_info->frozen[object_id])
4048 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4051 create a new object buffer.
4053 mng_info->ob[object_id]=(MngBuffer *)
4054 AcquireMagickMemory(sizeof(MngBuffer));
4056 if (mng_info->ob[object_id] != (MngBuffer *) NULL)
4058 mng_info->ob[object_id]->image=(Image *) NULL;
4059 mng_info->ob[object_id]->reference_count=1;
4063 if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
4064 mng_info->ob[object_id]->frozen)
4066 if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4067 png_error(ping,"Memory allocation failed");
4069 if (mng_info->ob[object_id]->frozen)
4070 png_error(ping,"Cannot overwrite frozen MNG object buffer");
4076 if (mng_info->ob[object_id]->image != (Image *) NULL)
4077 mng_info->ob[object_id]->image=DestroyImage
4078 (mng_info->ob[object_id]->image);
4080 mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4083 if (mng_info->ob[object_id]->image != (Image *) NULL)
4084 mng_info->ob[object_id]->image->file=(FILE *) NULL;
4087 png_error(ping, "Cloning image for object buffer failed");
4089 if (ping_width > 250000L || ping_height > 250000L)
4090 png_error(ping,"PNG Image dimensions are too large.");
4092 mng_info->ob[object_id]->width=ping_width;
4093 mng_info->ob[object_id]->height=ping_height;
4094 mng_info->ob[object_id]->color_type=ping_color_type;
4095 mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4096 mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4097 mng_info->ob[object_id]->compression_method=
4098 ping_compression_method;
4099 mng_info->ob[object_id]->filter_method=ping_filter_method;
4101 if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4107 Copy the PLTE to the object buffer.
4109 png_get_PLTE(ping,ping_info,&plte,&number_colors);
4110 mng_info->ob[object_id]->plte_length=number_colors;
4112 for (i=0; i < number_colors; i++)
4114 mng_info->ob[object_id]->plte[i]=plte[i];
4119 mng_info->ob[object_id]->plte_length=0;
4124 /* Set image->alpha_trait to MagickTrue if the input colortype supports
4125 * alpha or if a valid tRNS chunk is present, no matter whether there
4126 * is actual transparency present.
4128 image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4129 ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4130 (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4131 BlendPixelTrait : UndefinedPixelTrait;
4133 #if 0 /* I'm not sure what's wrong here but it does not work. */
4134 if (image->alpha_trait != UndefinedPixelTrait)
4136 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4137 (void) SetImageType(image,GrayscaleAlphaType,exception);
4139 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4140 (void) SetImageType(image,PaletteAlphaType,exception);
4143 (void) SetImageType(image,TrueColorAlphaType,exception);
4148 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4149 (void) SetImageType(image,GrayscaleType,exception);
4151 else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4152 (void) SetImageType(image,PaletteType,exception);
4155 (void) SetImageType(image,TrueColorType,exception);
4159 /* Set more properties for identify to retrieve */
4162 msg[MagickPathExtent];
4164 if (num_text_total != 0)
4166 /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4167 (void) FormatLocaleString(msg,MagickPathExtent,
4168 "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4169 (void) SetImageProperty(image,"png:text",msg,
4173 if (num_raw_profiles != 0)
4175 (void) FormatLocaleString(msg,MagickPathExtent,
4176 "%d were found", num_raw_profiles);
4177 (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4182 if (ping_found_cHRM != MagickFalse)
4184 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4185 "chunk was found (see Chromaticity, above)");
4186 (void) SetImageProperty(image,"png:cHRM",msg,
4191 if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4193 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4194 "chunk was found (see Background color, above)");
4195 (void) SetImageProperty(image,"png:bKGD",msg,
4199 (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4202 #if defined(PNG_iCCP_SUPPORTED)
4204 if (ping_found_iCCP != MagickFalse)
4205 (void) SetImageProperty(image,"png:iCCP",msg,
4209 if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4210 (void) SetImageProperty(image,"png:tRNS",msg,
4213 #if defined(PNG_sRGB_SUPPORTED)
4215 if (ping_found_sRGB != MagickFalse)
4217 (void) FormatLocaleString(msg,MagickPathExtent,
4220 Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4221 (void) SetImageProperty(image,"png:sRGB",msg,
4227 if (ping_found_gAMA != MagickFalse)
4229 (void) FormatLocaleString(msg,MagickPathExtent,
4230 "gamma=%.8g (See Gamma, above)",
4232 (void) SetImageProperty(image,"png:gAMA",msg,
4236 #if defined(PNG_pHYs_SUPPORTED)
4238 if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4240 (void) FormatLocaleString(msg,MagickPathExtent,
4241 "x_res=%.10g, y_res=%.10g, units=%d",
4242 (double) x_resolution,(double) y_resolution, unit_type);
4243 (void) SetImageProperty(image,"png:pHYs",msg,
4248 #if defined(PNG_oFFs_SUPPORTED)
4250 if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4252 (void) FormatLocaleString(msg,MagickPathExtent,
4253 "x_off=%.20g, y_off=%.20g",
4254 (double) image->page.x,(double) image->page.y);
4255 (void) SetImageProperty(image,"png:oFFs",msg,
4260 #if defined(PNG_tIME_SUPPORTED)
4261 read_tIME_chunk(image,ping,end_info,exception);
4265 if ((image->page.width != 0 && image->page.width != image->columns) ||
4266 (image->page.height != 0 && image->page.height != image->rows) ||
4267 (image->page.x != 0 || image->page.y != 0))
4269 (void) FormatLocaleString(msg,MagickPathExtent,
4270 "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4271 (double) image->page.width,(double) image->page.height,
4272 (double) image->page.x,(double) image->page.y);
4273 (void) SetImageProperty(image,"png:caNv",msg,
4278 if ((image->page.width != 0 && image->page.width != image->columns) ||
4279 (image->page.height != 0 && image->page.height != image->rows))
4281 (void) FormatLocaleString(msg,MagickPathExtent,
4282 "width=%.20g, height=%.20g",
4283 (double) image->page.width,(double) image->page.height);
4284 (void) SetImageProperty(image,"png:vpAg",msg,
4290 Relinquish resources.
4292 png_destroy_read_struct(&ping,&ping_info,&end_info);
4294 pixel_info=RelinquishVirtualMemory(pixel_info);
4296 if (logging != MagickFalse)
4297 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4298 " exit ReadOnePNGImage()");
4300 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4301 UnlockSemaphoreInfo(ping_semaphore);
4304 /* } for navigation to beginning of SETJMP-protected block, revert to
4305 * Throwing an Exception when an error occurs.
4310 /* end of reading one PNG image */
4313 static Image *ReadPNGImage(const ImageInfo *image_info,
4314 ExceptionInfo *exception)
4327 magic_number[MagickPathExtent];
4335 assert(image_info != (const ImageInfo *) NULL);
4336 assert(image_info->signature == MagickCoreSignature);
4338 if (image_info->debug != MagickFalse)
4339 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4340 image_info->filename);
4342 assert(exception != (ExceptionInfo *) NULL);
4343 assert(exception->signature == MagickCoreSignature);
4344 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4345 image=AcquireImage(image_info,exception);
4346 mng_info=(MngInfo *) NULL;
4347 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4349 if (status == MagickFalse)
4350 ThrowReaderException(FileOpenError,"UnableToOpenFile");
4353 Verify PNG signature.
4355 count=ReadBlob(image,8,(unsigned char *) magic_number);
4357 if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4358 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4361 Allocate a MngInfo structure.
4363 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4365 if (mng_info == (MngInfo *) NULL)
4366 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4369 Initialize members of the MngInfo structure.
4371 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4372 mng_info->image=image;
4374 image=ReadOnePNGImage(mng_info,image_info,exception);
4375 mng_info=MngInfoFreeStruct(mng_info);
4377 if (image == (Image *) NULL)
4379 if (logging != MagickFalse)
4380 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4381 "exit ReadPNGImage() with error");
4383 return((Image *) NULL);
4386 (void) CloseBlob(image);
4388 if ((image->columns == 0) || (image->rows == 0))
4390 if (logging != MagickFalse)
4391 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4392 "exit ReadPNGImage() with error.");
4394 ThrowReaderException(CorruptImageError,"CorruptImage");
4397 if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4398 ((image->gamma < .45) || (image->gamma > .46)) &&
4399 !(image->chromaticity.red_primary.x>0.6399f &&
4400 image->chromaticity.red_primary.x<0.6401f &&
4401 image->chromaticity.red_primary.y>0.3299f &&
4402 image->chromaticity.red_primary.y<0.3301f &&
4403 image->chromaticity.green_primary.x>0.2999f &&
4404 image->chromaticity.green_primary.x<0.3001f &&
4405 image->chromaticity.green_primary.y>0.5999f &&
4406 image->chromaticity.green_primary.y<0.6001f &&
4407 image->chromaticity.blue_primary.x>0.1499f &&
4408 image->chromaticity.blue_primary.x<0.1501f &&
4409 image->chromaticity.blue_primary.y>0.0599f &&
4410 image->chromaticity.blue_primary.y<0.0601f &&
4411 image->chromaticity.white_point.x>0.3126f &&
4412 image->chromaticity.white_point.x<0.3128f &&
4413 image->chromaticity.white_point.y>0.3289f &&
4414 image->chromaticity.white_point.y<0.3291f))
4416 SetImageColorspace(image,RGBColorspace,exception);
4419 if (logging != MagickFalse)
4421 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4422 " page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4423 (double) image->page.width,(double) image->page.height,
4424 (double) image->page.x,(double) image->page.y);
4425 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4426 " image->colorspace: %d", (int) image->colorspace);
4429 if (logging != MagickFalse)
4430 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4437 #if defined(JNG_SUPPORTED)
4439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4443 % R e a d O n e J N G I m a g e %
4447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4449 % ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4450 % (minus the 8-byte signature) and returns it. It allocates the memory
4451 % necessary for the new Image structure and returns a pointer to the new
4454 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
4456 % The format of the ReadOneJNGImage method is:
4458 % Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4459 % ExceptionInfo *exception)
4461 % A description of each parameter follows:
4463 % o mng_info: Specifies a pointer to a MngInfo structure.
4465 % o image_info: the image info.
4467 % o exception: return any errors or warnings in this structure.
4470 static Image *ReadOneJNGImage(MngInfo *mng_info,
4471 const ImageInfo *image_info, ExceptionInfo *exception)
4498 jng_image_sample_depth,
4499 jng_image_compression_method,
4500 jng_image_interlace_method,
4501 jng_alpha_sample_depth,
4502 jng_alpha_compression_method,
4503 jng_alpha_filter_method,
4504 jng_alpha_interlace_method;
4506 register const Quantum
4516 register unsigned char
4526 jng_alpha_compression_method=0;
4527 jng_alpha_sample_depth=8;
4531 alpha_image=(Image *) NULL;
4532 color_image=(Image *) NULL;
4533 alpha_image_info=(ImageInfo *) NULL;
4534 color_image_info=(ImageInfo *) NULL;
4536 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4537 " Enter ReadOneJNGImage()");
4539 image=mng_info->image;
4541 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4544 Allocate next image structure.
4546 if (logging != MagickFalse)
4547 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4548 " AcquireNextImage()");
4550 AcquireNextImage(image_info,image,exception);
4552 if (GetNextImageInList(image) == (Image *) NULL)
4553 return(DestroyImageList(image));
4555 image=SyncNextImageInList(image);
4557 mng_info->image=image;
4560 Signature bytes have already been read.
4563 read_JSEP=MagickFalse;
4564 reading_idat=MagickFalse;
4568 type[MagickPathExtent];
4577 Read a new JNG chunk.
4579 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4580 2*GetBlobSize(image));
4582 if (status == MagickFalse)
4586 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4587 length=ReadBlobMSBLong(image);
4588 count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4590 if (logging != MagickFalse)
4591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4592 " Reading JNG chunk type %c%c%c%c, length: %.20g",
4593 type[0],type[1],type[2],type[3],(double) length);
4595 if (length > PNG_UINT_31_MAX || count == 0)
4596 ThrowReaderException(CorruptImageError,"CorruptImage");
4599 chunk=(unsigned char *) NULL;
4603 chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4605 if (chunk == (unsigned char *) NULL)
4606 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4608 for (i=0; i < (ssize_t) length; i++)
4609 chunk[i]=(unsigned char) ReadBlobByte(image);
4614 (void) ReadBlobMSBLong(image); /* read crc word */
4616 if (memcmp(type,mng_JHDR,4) == 0)
4620 jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4621 (p[2] << 8) | p[3]);
4622 jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4623 (p[6] << 8) | p[7]);
4624 if ((jng_width == 0) || (jng_height == 0))
4625 ThrowReaderException(CorruptImageError,
4626 "NegativeOrZeroImageSize");
4627 jng_color_type=p[8];
4628 jng_image_sample_depth=p[9];
4629 jng_image_compression_method=p[10];
4630 jng_image_interlace_method=p[11];
4632 image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4635 jng_alpha_sample_depth=p[12];
4636 jng_alpha_compression_method=p[13];
4637 jng_alpha_filter_method=p[14];
4638 jng_alpha_interlace_method=p[15];
4640 if (logging != MagickFalse)
4642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4643 " jng_width: %16lu, jng_height: %16lu\n"
4644 " jng_color_type: %16d, jng_image_sample_depth: %3d\n"
4645 " jng_image_compression_method:%3d",
4646 (unsigned long) jng_width, (unsigned long) jng_height,
4647 jng_color_type, jng_image_sample_depth,
4648 jng_image_compression_method);
4650 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4651 " jng_image_interlace_method: %3d"
4652 " jng_alpha_sample_depth: %3d",
4653 jng_image_interlace_method,
4654 jng_alpha_sample_depth);
4656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4657 " jng_alpha_compression_method:%3d\n"
4658 " jng_alpha_filter_method: %3d\n"
4659 " jng_alpha_interlace_method: %3d",
4660 jng_alpha_compression_method,
4661 jng_alpha_filter_method,
4662 jng_alpha_interlace_method);
4667 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4673 if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4674 ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4675 (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4678 o create color_image
4679 o open color_blob, attached to color_image
4680 o if (color type has alpha)
4681 open alpha_blob, attached to alpha_image
4684 color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4686 if (color_image_info == (ImageInfo *) NULL)
4687 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4689 GetImageInfo(color_image_info);
4690 color_image=AcquireImage(color_image_info,exception);
4692 if (color_image == (Image *) NULL)
4693 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4695 if (logging != MagickFalse)
4696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4697 " Creating color_blob.");
4699 (void) AcquireUniqueFilename(color_image->filename);
4700 status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4703 if (status == MagickFalse)
4705 color_image=DestroyImage(color_image);
4706 return(DestroyImageList(image));
4709 if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4711 alpha_image_info=(ImageInfo *)
4712 AcquireMagickMemory(sizeof(ImageInfo));
4714 if (alpha_image_info == (ImageInfo *) NULL)
4716 color_image=DestroyImage(color_image);
4717 ThrowReaderException(ResourceLimitError,
4718 "MemoryAllocationFailed");
4721 GetImageInfo(alpha_image_info);
4722 alpha_image=AcquireImage(alpha_image_info,exception);
4724 if (alpha_image == (Image *) NULL)
4726 alpha_image_info=DestroyImageInfo(alpha_image_info);
4727 color_image=DestroyImage(color_image);
4728 ThrowReaderException(ResourceLimitError,
4729 "MemoryAllocationFailed");
4732 if (logging != MagickFalse)
4733 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4734 " Creating alpha_blob.");
4736 (void) AcquireUniqueFilename(alpha_image->filename);
4737 status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4740 if (status == MagickFalse)
4742 alpha_image=DestroyImage(alpha_image);
4743 alpha_image_info=DestroyImageInfo(alpha_image_info);
4744 color_image=DestroyImage(color_image);
4745 return(DestroyImageList(image));
4748 if (jng_alpha_compression_method == 0)
4753 if (logging != MagickFalse)
4754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4755 " Writing IHDR chunk to alpha_blob.");
4757 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4758 "\211PNG\r\n\032\n");
4760 (void) WriteBlobMSBULong(alpha_image,13L);
4761 PNGType(data,mng_IHDR);
4762 LogPNGChunk(logging,mng_IHDR,13L);
4763 PNGLong(data+4,jng_width);
4764 PNGLong(data+8,jng_height);
4765 data[12]=jng_alpha_sample_depth;
4766 data[13]=0; /* color_type gray */
4767 data[14]=0; /* compression method 0 */
4768 data[15]=0; /* filter_method 0 */
4769 data[16]=0; /* interlace_method 0 */
4770 (void) WriteBlob(alpha_image,17,data);
4771 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4774 reading_idat=MagickTrue;
4777 if (memcmp(type,mng_JDAT,4) == 0)
4779 /* Copy chunk to color_image->blob */
4781 if (logging != MagickFalse)
4782 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4783 " Copying JDAT chunk data to color_blob.");
4787 (void) WriteBlob(color_image,length,chunk);
4788 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4794 if (memcmp(type,mng_IDAT,4) == 0)
4799 /* Copy IDAT header and chunk data to alpha_image->blob */
4801 if (alpha_image != NULL && image_info->ping == MagickFalse)
4803 if (logging != MagickFalse)
4804 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4805 " Copying IDAT chunk data to alpha_blob.");
4807 (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4808 PNGType(data,mng_IDAT);
4809 LogPNGChunk(logging,mng_IDAT,length);
4810 (void) WriteBlob(alpha_image,4,data);
4811 (void) WriteBlob(alpha_image,length,chunk);
4812 (void) WriteBlobMSBULong(alpha_image,
4813 crc32(crc32(0,data,4),chunk,(uInt) length));
4817 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4822 if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4824 /* Copy chunk data to alpha_image->blob */
4826 if (alpha_image != NULL && image_info->ping == MagickFalse)
4828 if (logging != MagickFalse)
4829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4830 " Copying JDAA chunk data to alpha_blob.");
4832 (void) WriteBlob(alpha_image,length,chunk);
4836 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4841 if (memcmp(type,mng_JSEP,4) == 0)
4843 read_JSEP=MagickTrue;
4846 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4851 if (memcmp(type,mng_bKGD,4) == 0)
4855 image->background_color.red=ScaleCharToQuantum(p[1]);
4856 image->background_color.green=image->background_color.red;
4857 image->background_color.blue=image->background_color.red;
4862 image->background_color.red=ScaleCharToQuantum(p[1]);
4863 image->background_color.green=ScaleCharToQuantum(p[3]);
4864 image->background_color.blue=ScaleCharToQuantum(p[5]);
4867 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4871 if (memcmp(type,mng_gAMA,4) == 0)
4874 image->gamma=((float) mng_get_long(p))*0.00001;
4876 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4880 if (memcmp(type,mng_cHRM,4) == 0)
4884 image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4885 image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4886 image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4887 image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4888 image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4889 image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4890 image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4891 image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4894 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4898 if (memcmp(type,mng_sRGB,4) == 0)
4902 image->rendering_intent=
4903 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4904 image->gamma=1.000f/2.200f;
4905 image->chromaticity.red_primary.x=0.6400f;
4906 image->chromaticity.red_primary.y=0.3300f;
4907 image->chromaticity.green_primary.x=0.3000f;
4908 image->chromaticity.green_primary.y=0.6000f;
4909 image->chromaticity.blue_primary.x=0.1500f;
4910 image->chromaticity.blue_primary.y=0.0600f;
4911 image->chromaticity.white_point.x=0.3127f;
4912 image->chromaticity.white_point.y=0.3290f;
4915 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4919 if (memcmp(type,mng_oFFs,4) == 0)
4923 image->page.x=(ssize_t) mng_get_long(p);
4924 image->page.y=(ssize_t) mng_get_long(&p[4]);
4926 if ((int) p[8] != 0)
4928 image->page.x/=10000;
4929 image->page.y/=10000;
4934 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4939 if (memcmp(type,mng_pHYs,4) == 0)
4943 image->resolution.x=(double) mng_get_long(p);
4944 image->resolution.y=(double) mng_get_long(&p[4]);
4945 if ((int) p[8] == PNG_RESOLUTION_METER)
4947 image->units=PixelsPerCentimeterResolution;
4948 image->resolution.x=image->resolution.x/100.0f;
4949 image->resolution.y=image->resolution.y/100.0f;
4953 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4958 if (memcmp(type,mng_iCCP,4) == 0)
4962 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4969 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4971 if (memcmp(type,mng_IEND,4))
4981 Finish up reading image data:
4983 o read main image from color_blob.
4987 o if (color_type has alpha)
4988 if alpha_encoding is PNG
4989 read secondary image from alpha_blob via ReadPNG
4990 if alpha_encoding is JPEG
4991 read secondary image from alpha_blob via ReadJPEG
4995 o copy intensity of secondary image into
4996 alpha samples of main image.
4998 o destroy the secondary image.
5001 if (color_image_info == (ImageInfo *) NULL)
5003 assert(color_image == (Image *) NULL);
5004 assert(alpha_image == (Image *) NULL);
5005 return(DestroyImageList(image));
5008 if (color_image == (Image *) NULL)
5010 assert(alpha_image == (Image *) NULL);
5011 return(DestroyImageList(image));
5014 (void) SeekBlob(color_image,0,SEEK_SET);
5016 if (logging != MagickFalse)
5017 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5018 " Reading jng_image from color_blob.");
5020 assert(color_image_info != (ImageInfo *) NULL);
5021 (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
5022 color_image->filename);
5024 color_image_info->ping=MagickFalse; /* To do: avoid this */
5025 jng_image=ReadImage(color_image_info,exception);
5027 (void) RelinquishUniqueFileResource(color_image->filename);
5028 color_image=DestroyImage(color_image);
5029 color_image_info=DestroyImageInfo(color_image_info);
5031 if (jng_image == (Image *) NULL)
5032 return(DestroyImageList(image));
5034 if (logging != MagickFalse)
5035 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5036 " Copying jng_image pixels to main image.");
5038 image->rows=jng_height;
5039 image->columns=jng_width;
5041 status=SetImageExtent(image,image->columns,image->rows,exception);
5042 if (status == MagickFalse)
5043 return(DestroyImageList(image));
5045 for (y=0; y < (ssize_t) image->rows; y++)
5047 s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5048 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5049 for (x=(ssize_t) image->columns; x != 0; x--)
5051 SetPixelRed(image,GetPixelRed(jng_image,s),q);
5052 SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5053 SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5054 q+=GetPixelChannels(image);
5055 s+=GetPixelChannels(jng_image);
5058 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5062 jng_image=DestroyImage(jng_image);
5064 if (image_info->ping == MagickFalse)
5066 if (jng_color_type >= 12)
5068 if (jng_alpha_compression_method == 0)
5072 (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5073 PNGType(data,mng_IEND);
5074 LogPNGChunk(logging,mng_IEND,0L);
5075 (void) WriteBlob(alpha_image,4,data);
5076 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5079 (void) CloseBlob(alpha_image);
5081 if (logging != MagickFalse)
5082 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5083 " Reading alpha from alpha_blob.");
5085 (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5086 "%s",alpha_image->filename);
5088 jng_image=ReadImage(alpha_image_info,exception);
5090 if (jng_image != (Image *) NULL)
5091 for (y=0; y < (ssize_t) image->rows; y++)
5093 s=GetVirtualPixels(jng_image,0,y,image->columns,1,
5095 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5097 if (image->alpha_trait != UndefinedPixelTrait)
5098 for (x=(ssize_t) image->columns; x != 0; x--)
5100 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5101 q+=GetPixelChannels(image);
5102 s+=GetPixelChannels(jng_image);
5106 for (x=(ssize_t) image->columns; x != 0; x--)
5108 SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5109 if (GetPixelAlpha(image,q) != OpaqueAlpha)
5110 image->alpha_trait=BlendPixelTrait;
5111 q+=GetPixelChannels(image);
5112 s+=GetPixelChannels(jng_image);
5115 if (SyncAuthenticPixels(image,exception) == MagickFalse)
5118 (void) RelinquishUniqueFileResource(alpha_image->filename);
5119 alpha_image=DestroyImage(alpha_image);
5120 alpha_image_info=DestroyImageInfo(alpha_image_info);
5121 if (jng_image != (Image *) NULL)
5122 jng_image=DestroyImage(jng_image);
5126 /* Read the JNG image. */
5128 if (mng_info->mng_type == 0)
5130 mng_info->mng_width=jng_width;
5131 mng_info->mng_height=jng_height;
5134 if (image->page.width == 0 && image->page.height == 0)
5136 image->page.width=jng_width;
5137 image->page.height=jng_height;
5140 if (image->page.x == 0 && image->page.y == 0)
5142 image->page.x=mng_info->x_off[mng_info->object_id];
5143 image->page.y=mng_info->y_off[mng_info->object_id];
5148 image->page.y=mng_info->y_off[mng_info->object_id];
5151 mng_info->image_found++;
5152 status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5153 2*GetBlobSize(image));
5155 if (status == MagickFalse)
5156 return(DestroyImageList(image));
5158 if (logging != MagickFalse)
5159 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5160 " exit ReadOneJNGImage()");
5166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5170 % R e a d J N G I m a g e %
5174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5176 % ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5177 % (including the 8-byte signature) and returns it. It allocates the memory
5178 % necessary for the new Image structure and returns a pointer to the new
5181 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
5183 % The format of the ReadJNGImage method is:
5185 % Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5188 % A description of each parameter follows:
5190 % o image_info: the image info.
5192 % o exception: return any errors or warnings in this structure.
5196 static Image *ReadJNGImage(const ImageInfo *image_info,
5197 ExceptionInfo *exception)
5210 magic_number[MagickPathExtent];
5218 assert(image_info != (const ImageInfo *) NULL);
5219 assert(image_info->signature == MagickCoreSignature);
5220 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5221 image_info->filename);
5222 assert(exception != (ExceptionInfo *) NULL);
5223 assert(exception->signature == MagickCoreSignature);
5224 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5225 image=AcquireImage(image_info,exception);
5226 mng_info=(MngInfo *) NULL;
5227 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5229 if (status == MagickFalse)
5230 return((Image *) NULL);
5232 if (LocaleCompare(image_info->magick,"JNG") != 0)
5233 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5235 /* Verify JNG signature. */
5237 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5239 if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5240 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5242 /* Allocate a MngInfo structure. */
5244 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5246 if (mng_info == (MngInfo *) NULL)
5247 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5249 /* Initialize members of the MngInfo structure. */
5251 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5253 mng_info->image=image;
5254 image=ReadOneJNGImage(mng_info,image_info,exception);
5255 mng_info=MngInfoFreeStruct(mng_info);
5257 if (image == (Image *) NULL)
5259 if (logging != MagickFalse)
5260 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5261 "exit ReadJNGImage() with error");
5263 return((Image *) NULL);
5265 (void) CloseBlob(image);
5267 if (image->columns == 0 || image->rows == 0)
5269 if (logging != MagickFalse)
5270 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5271 "exit ReadJNGImage() with error");
5273 ThrowReaderException(CorruptImageError,"CorruptImage");
5276 if (logging != MagickFalse)
5277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5283 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5284 ExceptionInfo *exception)
5287 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 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5381 " Enter ReadOneMNGImage()");
5383 image=mng_info->image;
5385 if (LocaleCompare(image_info->magick,"MNG") == 0)
5388 magic_number[MagickPathExtent];
5390 /* Verify MNG signature. */
5391 count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5392 if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5393 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5395 /* Initialize some nonzero members of the MngInfo structure. */
5396 for (i=0; i < MNG_MAX_OBJECTS; i++)
5398 mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5399 mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5401 mng_info->exists[0]=MagickTrue;
5405 first_mng_object=MagickTrue;
5407 #if defined(MNG_INSERT_LAYERS)
5408 insert_layers=MagickFalse; /* should be False during convert or mogrify */
5410 default_frame_delay=0;
5411 default_frame_timeout=0;
5414 mng_info->ticks_per_second=1UL*image->ticks_per_second;
5416 skip_to_iend=MagickFalse;
5417 term_chunk_found=MagickFalse;
5418 mng_info->framing_mode=1;
5419 #if defined(MNG_INSERT_LAYERS)
5420 mandatory_back=MagickFalse;
5422 #if defined(MNG_INSERT_LAYERS)
5423 mng_background_color=image->background_color;
5425 default_fb=mng_info->frame;
5426 previous_fb=mng_info->frame;
5430 type[MagickPathExtent];
5432 if (LocaleCompare(image_info->magick,"MNG") == 0)
5441 (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5442 length=ReadBlobMSBLong(image);
5443 count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5445 if (logging != MagickFalse)
5446 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5447 " Reading MNG chunk type %c%c%c%c, length: %.20g",
5448 type[0],type[1],type[2],type[3],(double) length);
5450 if (length > PNG_UINT_31_MAX)
5457 ThrowReaderException(CorruptImageError,"CorruptImage");
5460 chunk=(unsigned char *) NULL;
5464 chunk=(unsigned char *) AcquireQuantumMemory(length,
5467 if (chunk == (unsigned char *) NULL)
5468 ThrowReaderException(ResourceLimitError,
5469 "MemoryAllocationFailed");
5471 for (i=0; i < (ssize_t) length; i++)
5472 chunk[i]=(unsigned char) ReadBlobByte(image);
5477 (void) ReadBlobMSBLong(image); /* read crc word */
5479 #if !defined(JNG_SUPPORTED)
5480 if (memcmp(type,mng_JHDR,4) == 0)
5482 skip_to_iend=MagickTrue;
5484 if (mng_info->jhdr_warning == 0)
5485 (void) ThrowMagickException(exception,GetMagickModule(),
5486 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5488 mng_info->jhdr_warning++;
5491 if (memcmp(type,mng_DHDR,4) == 0)
5493 skip_to_iend=MagickTrue;
5495 if (mng_info->dhdr_warning == 0)
5496 (void) ThrowMagickException(exception,GetMagickModule(),
5497 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5499 mng_info->dhdr_warning++;
5501 if (memcmp(type,mng_MEND,4) == 0)
5506 if (memcmp(type,mng_IEND,4) == 0)
5507 skip_to_iend=MagickFalse;
5510 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5512 if (logging != MagickFalse)
5513 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5519 if (memcmp(type,mng_MHDR,4) == 0)
5523 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5524 ThrowReaderException(CorruptImageError,"CorruptImage");
5527 mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5528 (p[2] << 8) | p[3]);
5530 mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5531 (p[6] << 8) | p[7]);
5533 if (logging != MagickFalse)
5535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5536 " MNG width: %.20g",(double) mng_info->mng_width);
5537 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5538 " MNG height: %.20g",(double) mng_info->mng_height);
5542 mng_info->ticks_per_second=(size_t) mng_get_long(p);
5544 if (mng_info->ticks_per_second == 0)
5545 default_frame_delay=0;
5548 default_frame_delay=1UL*image->ticks_per_second/
5549 mng_info->ticks_per_second;
5551 frame_delay=default_frame_delay;
5555 simplicity=(size_t) mng_get_long(p);
5557 mng_type=1; /* Full MNG */
5559 if ((simplicity != 0) && ((simplicity | 11) == 11))
5560 mng_type=2; /* LC */
5562 if ((simplicity != 0) && ((simplicity | 9) == 9))
5563 mng_type=3; /* VLC */
5565 #if defined(MNG_INSERT_LAYERS)
5567 insert_layers=MagickTrue;
5569 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5571 /* Allocate next image structure. */
5572 AcquireNextImage(image_info,image,exception);
5574 if (GetNextImageInList(image) == (Image *) NULL)
5575 return((Image *) NULL);
5577 image=SyncNextImageInList(image);
5578 mng_info->image=image;
5581 if ((mng_info->mng_width > 65535L) ||
5582 (mng_info->mng_height > 65535L))
5584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5585 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5588 (void) FormatLocaleString(page_geometry,MagickPathExtent,
5589 "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5590 mng_info->mng_height);
5592 mng_info->frame.left=0;
5593 mng_info->frame.right=(ssize_t) mng_info->mng_width;
5594 mng_info->frame.top=0;
5595 mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5596 mng_info->clip=default_fb=previous_fb=mng_info->frame;
5598 for (i=0; i < MNG_MAX_OBJECTS; i++)
5599 mng_info->object_clip[i]=mng_info->frame;
5601 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5605 if (memcmp(type,mng_TERM,4) == 0)
5615 final_delay=(png_uint_32) mng_get_long(&p[2]);
5616 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5618 if (mng_iterations == PNG_UINT_31_MAX)
5621 image->iterations=mng_iterations;
5622 term_chunk_found=MagickTrue;
5625 if (logging != MagickFalse)
5627 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5628 " repeat=%d, final_delay=%.20g, iterations=%.20g",
5629 repeat,(double) final_delay, (double) image->iterations);
5632 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5635 if (memcmp(type,mng_DEFI,4) == 0)
5638 (void) ThrowMagickException(exception,GetMagickModule(),
5639 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5645 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5646 ThrowReaderException(CorruptImageError,"CorruptImage");
5649 object_id=(p[0] << 8) | p[1];
5651 if (mng_type == 2 && object_id != 0)
5652 (void) ThrowMagickException(exception,GetMagickModule(),
5653 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5656 if (object_id > MNG_MAX_OBJECTS)
5659 Instead of using a warning we should allocate a larger
5660 MngInfo structure and continue.
5662 (void) ThrowMagickException(exception,GetMagickModule(),
5663 CoderError,"object id too large","`%s'",image->filename);
5664 object_id=MNG_MAX_OBJECTS;
5667 if (mng_info->exists[object_id])
5668 if (mng_info->frozen[object_id])
5670 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5671 (void) ThrowMagickException(exception,
5672 GetMagickModule(),CoderError,
5673 "DEFI cannot redefine a frozen MNG object","`%s'",
5678 mng_info->exists[object_id]=MagickTrue;
5681 mng_info->invisible[object_id]=p[2];
5684 Extract object offset info.
5688 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5689 (p[5] << 16) | (p[6] << 8) | p[7]);
5691 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5692 (p[9] << 16) | (p[10] << 8) | p[11]);
5694 if (logging != MagickFalse)
5696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5697 " x_off[%d]: %.20g, y_off[%d]: %.20g",
5698 object_id,(double) mng_info->x_off[object_id],
5699 object_id,(double) mng_info->y_off[object_id]);
5704 Extract object clipping info.
5707 mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5710 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5713 if (memcmp(type,mng_bKGD,4) == 0)
5715 mng_info->have_global_bkgd=MagickFalse;
5719 mng_info->mng_global_bkgd.red=
5720 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5722 mng_info->mng_global_bkgd.green=
5723 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5725 mng_info->mng_global_bkgd.blue=
5726 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5728 mng_info->have_global_bkgd=MagickTrue;
5731 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5734 if (memcmp(type,mng_BACK,4) == 0)
5736 #if defined(MNG_INSERT_LAYERS)
5738 mandatory_back=p[6];
5743 if (mandatory_back && length > 5)
5745 mng_background_color.red=
5746 ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5748 mng_background_color.green=
5749 ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5751 mng_background_color.blue=
5752 ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5754 mng_background_color.alpha=OpaqueAlpha;
5757 #ifdef MNG_OBJECT_BUFFERS
5759 mng_background_object=(p[7] << 8) | p[8];
5762 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5766 if (memcmp(type,mng_PLTE,4) == 0)
5768 /* Read global PLTE. */
5770 if (length && (length < 769))
5772 if (mng_info->global_plte == (png_colorp) NULL)
5773 mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5774 sizeof(*mng_info->global_plte));
5776 for (i=0; i < (ssize_t) (length/3); i++)
5778 mng_info->global_plte[i].red=p[3*i];
5779 mng_info->global_plte[i].green=p[3*i+1];
5780 mng_info->global_plte[i].blue=p[3*i+2];
5783 mng_info->global_plte_length=(unsigned int) (length/3);
5786 for ( ; i < 256; i++)
5788 mng_info->global_plte[i].red=i;
5789 mng_info->global_plte[i].green=i;
5790 mng_info->global_plte[i].blue=i;
5794 mng_info->global_plte_length=256;
5797 mng_info->global_plte_length=0;
5799 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5803 if (memcmp(type,mng_tRNS,4) == 0)
5805 /* read global tRNS */
5807 if (length > 0 && length < 257)
5808 for (i=0; i < (ssize_t) length; i++)
5809 mng_info->global_trns[i]=p[i];
5812 for ( ; i < 256; i++)
5813 mng_info->global_trns[i]=255;
5815 mng_info->global_trns_length=(unsigned int) length;
5816 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5819 if (memcmp(type,mng_gAMA,4) == 0)
5826 igamma=mng_get_long(p);
5827 mng_info->global_gamma=((float) igamma)*0.00001;
5828 mng_info->have_global_gama=MagickTrue;
5832 mng_info->have_global_gama=MagickFalse;
5834 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5838 if (memcmp(type,mng_cHRM,4) == 0)
5840 /* Read global cHRM */
5844 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5845 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5846 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5847 mng_info->global_chrm.red_primary.y=0.00001*
5848 mng_get_long(&p[12]);
5849 mng_info->global_chrm.green_primary.x=0.00001*
5850 mng_get_long(&p[16]);
5851 mng_info->global_chrm.green_primary.y=0.00001*
5852 mng_get_long(&p[20]);
5853 mng_info->global_chrm.blue_primary.x=0.00001*
5854 mng_get_long(&p[24]);
5855 mng_info->global_chrm.blue_primary.y=0.00001*
5856 mng_get_long(&p[28]);
5857 mng_info->have_global_chrm=MagickTrue;
5860 mng_info->have_global_chrm=MagickFalse;
5862 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5866 if (memcmp(type,mng_sRGB,4) == 0)
5873 mng_info->global_srgb_intent=
5874 Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5875 mng_info->have_global_srgb=MagickTrue;
5878 mng_info->have_global_srgb=MagickFalse;
5880 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5884 if (memcmp(type,mng_iCCP,4) == 0)
5892 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5897 if (memcmp(type,mng_FRAM,4) == 0)
5900 (void) ThrowMagickException(exception,GetMagickModule(),
5901 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5904 if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5905 image->delay=frame_delay;
5907 frame_delay=default_frame_delay;
5908 frame_timeout=default_frame_timeout;
5913 mng_info->framing_mode=p[0];
5915 if (logging != MagickFalse)
5916 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5917 " Framing_mode=%d",mng_info->framing_mode);
5921 /* Note the delay and frame clipping boundaries. */
5923 p++; /* framing mode */
5925 while (*p && ((p-chunk) < (ssize_t) length))
5926 p++; /* frame name */
5928 p++; /* frame name terminator */
5930 if ((p-chunk) < (ssize_t) (length-4))
5937 change_delay=(*p++);
5938 change_timeout=(*p++);
5939 change_clipping=(*p++);
5940 p++; /* change_sync */
5944 frame_delay=1UL*image->ticks_per_second*
5947 if (mng_info->ticks_per_second != 0)
5948 frame_delay/=mng_info->ticks_per_second;
5951 frame_delay=PNG_UINT_31_MAX;
5953 if (change_delay == 2)
5954 default_frame_delay=frame_delay;
5958 if (logging != MagickFalse)
5959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5960 " Framing_delay=%.20g",(double) frame_delay);
5965 frame_timeout=1UL*image->ticks_per_second*
5968 if (mng_info->ticks_per_second != 0)
5969 frame_timeout/=mng_info->ticks_per_second;
5972 frame_timeout=PNG_UINT_31_MAX;
5974 if (change_timeout == 2)
5975 default_frame_timeout=frame_timeout;
5979 if (logging != MagickFalse)
5980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5981 " Framing_timeout=%.20g",(double) frame_timeout);
5984 if (change_clipping)
5986 fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5990 if (logging != MagickFalse)
5991 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5992 " Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5993 (double) fb.left,(double) fb.right,(double) fb.top,
5994 (double) fb.bottom);
5996 if (change_clipping == 2)
6002 mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6004 subframe_width=(size_t) (mng_info->clip.right
6005 -mng_info->clip.left);
6007 subframe_height=(size_t) (mng_info->clip.bottom
6008 -mng_info->clip.top);
6010 Insert a background layer behind the frame if framing_mode is 4.
6012 #if defined(MNG_INSERT_LAYERS)
6013 if (logging != MagickFalse)
6014 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6015 " subframe_width=%.20g, subframe_height=%.20g",(double)
6016 subframe_width,(double) subframe_height);
6018 if (insert_layers && (mng_info->framing_mode == 4) &&
6019 (subframe_width) && (subframe_height))
6021 /* Allocate next image structure. */
6022 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6024 AcquireNextImage(image_info,image,exception);
6026 if (GetNextImageInList(image) == (Image *) NULL)
6027 return(DestroyImageList(image));
6029 image=SyncNextImageInList(image);
6032 mng_info->image=image;
6034 if (term_chunk_found)
6036 image->start_loop=MagickTrue;
6037 image->iterations=mng_iterations;
6038 term_chunk_found=MagickFalse;
6042 image->start_loop=MagickFalse;
6044 image->columns=subframe_width;
6045 image->rows=subframe_height;
6046 image->page.width=subframe_width;
6047 image->page.height=subframe_height;
6048 image->page.x=mng_info->clip.left;
6049 image->page.y=mng_info->clip.top;
6050 image->background_color=mng_background_color;
6051 image->alpha_trait=UndefinedPixelTrait;
6053 (void) SetImageBackgroundColor(image,exception);
6055 if (logging != MagickFalse)
6056 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6057 " Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6058 (double) mng_info->clip.left,
6059 (double) mng_info->clip.right,
6060 (double) mng_info->clip.top,
6061 (double) mng_info->clip.bottom);
6064 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6068 if (memcmp(type,mng_CLIP,4) == 0)
6079 first_object=(p[0] << 8) | p[1];
6080 last_object=(p[2] << 8) | p[3];
6083 for (i=(int) first_object; i <= (int) last_object; i++)
6085 if (mng_info->exists[i] && !mng_info->frozen[i])
6090 box=mng_info->object_clip[i];
6091 if ((p-chunk) < (ssize_t) (length-17))
6092 mng_info->object_clip[i]=
6093 mng_read_box(box,(char) p[0],&p[1]);
6098 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6102 if (memcmp(type,mng_SAVE,4) == 0)
6104 for (i=1; i < MNG_MAX_OBJECTS; i++)
6105 if (mng_info->exists[i])
6107 mng_info->frozen[i]=MagickTrue;
6108 #ifdef MNG_OBJECT_BUFFERS
6109 if (mng_info->ob[i] != (MngBuffer *) NULL)
6110 mng_info->ob[i]->frozen=MagickTrue;
6115 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6120 if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6122 /* Read DISC or SEEK. */
6124 if ((length == 0) || !memcmp(type,mng_SEEK,4))
6126 for (i=1; i < MNG_MAX_OBJECTS; i++)
6127 MngInfoDiscardObject(mng_info,i);
6135 for (j=1; j < (ssize_t) length; j+=2)
6137 i=p[j-1] << 8 | p[j];
6138 MngInfoDiscardObject(mng_info,i);
6143 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6148 if (memcmp(type,mng_MOVE,4) == 0)
6158 first_object=(p[0] << 8) | p[1];
6159 last_object=(p[2] << 8) | p[3];
6162 for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6164 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6165 (p-chunk) < (ssize_t) (length-8))
6173 old_pair.a=mng_info->x_off[i];
6174 old_pair.b=mng_info->y_off[i];
6175 new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6176 mng_info->x_off[i]=new_pair.a;
6177 mng_info->y_off[i]=new_pair.b;
6182 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6186 if (memcmp(type,mng_LOOP,4) == 0)
6188 ssize_t loop_iters=1;
6191 loop_level=chunk[0];
6192 mng_info->loop_active[loop_level]=1; /* mark loop active */
6194 /* Record starting point. */
6195 loop_iters=mng_get_long(&chunk[1]);
6197 if (logging != MagickFalse)
6198 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6199 " LOOP level %.20g has %.20g iterations ",
6200 (double) loop_level, (double) loop_iters);
6202 if (loop_iters == 0)
6203 skipping_loop=loop_level;
6207 mng_info->loop_jump[loop_level]=TellBlob(image);
6208 mng_info->loop_count[loop_level]=loop_iters;
6211 mng_info->loop_iteration[loop_level]=0;
6213 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6217 if (memcmp(type,mng_ENDL,4) == 0)
6221 loop_level=chunk[0];
6223 if (skipping_loop > 0)
6225 if (skipping_loop == loop_level)
6228 Found end of zero-iteration loop.
6231 mng_info->loop_active[loop_level]=0;
6237 if (mng_info->loop_active[loop_level] == 1)
6239 mng_info->loop_count[loop_level]--;
6240 mng_info->loop_iteration[loop_level]++;
6242 if (logging != MagickFalse)
6243 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6244 " ENDL: LOOP level %.20g has %.20g remaining iters",
6245 (double) loop_level,(double)
6246 mng_info->loop_count[loop_level]);
6248 if (mng_info->loop_count[loop_level] != 0)
6251 SeekBlob(image,mng_info->loop_jump[loop_level],
6256 chunk=(unsigned char *) RelinquishMagickMemory(
6258 ThrowReaderException(CorruptImageError,
6259 "ImproperImageHeader");
6271 mng_info->loop_active[loop_level]=0;
6273 for (i=0; i < loop_level; i++)
6274 if (mng_info->loop_active[i] == 1)
6275 last_level=(short) i;
6276 loop_level=last_level;
6282 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6286 if (memcmp(type,mng_CLON,4) == 0)
6288 if (mng_info->clon_warning == 0)
6289 (void) ThrowMagickException(exception,GetMagickModule(),
6290 CoderError,"CLON is not implemented yet","`%s'",
6293 mng_info->clon_warning++;
6296 if (memcmp(type,mng_MAGN,4) == 0)
6311 magn_first=(p[0] << 8) | p[1];
6317 magn_last=(p[2] << 8) | p[3];
6320 magn_last=magn_first;
6321 #ifndef MNG_OBJECT_BUFFERS
6322 if (magn_first || magn_last)
6323 if (mng_info->magn_warning == 0)
6325 (void) ThrowMagickException(exception,
6326 GetMagickModule(),CoderError,
6327 "MAGN is not implemented yet for nonzero objects",
6328 "`%s'",image->filename);
6330 mng_info->magn_warning++;
6340 magn_mx=(p[5] << 8) | p[6];
6349 magn_my=(p[7] << 8) | p[8];
6358 magn_ml=(p[9] << 8) | p[10];
6367 magn_mr=(p[11] << 8) | p[12];
6376 magn_mt=(p[13] << 8) | p[14];
6385 magn_mb=(p[15] << 8) | p[16];
6397 magn_methy=magn_methx;
6400 if (magn_methx > 5 || magn_methy > 5)
6401 if (mng_info->magn_warning == 0)
6403 (void) ThrowMagickException(exception,
6404 GetMagickModule(),CoderError,
6405 "Unknown MAGN method in MNG datastream","`%s'",
6408 mng_info->magn_warning++;
6410 #ifdef MNG_OBJECT_BUFFERS
6411 /* Magnify existing objects in the range magn_first to magn_last */
6413 if (magn_first == 0 || magn_last == 0)
6415 /* Save the magnification factors for object 0 */
6416 mng_info->magn_mb=magn_mb;
6417 mng_info->magn_ml=magn_ml;
6418 mng_info->magn_mr=magn_mr;
6419 mng_info->magn_mt=magn_mt;
6420 mng_info->magn_mx=magn_mx;
6421 mng_info->magn_my=magn_my;
6422 mng_info->magn_methx=magn_methx;
6423 mng_info->magn_methy=magn_methy;
6427 if (memcmp(type,mng_PAST,4) == 0)
6429 if (mng_info->past_warning == 0)
6430 (void) ThrowMagickException(exception,GetMagickModule(),
6431 CoderError,"PAST is not implemented yet","`%s'",
6434 mng_info->past_warning++;
6437 if (memcmp(type,mng_SHOW,4) == 0)
6439 if (mng_info->show_warning == 0)
6440 (void) ThrowMagickException(exception,GetMagickModule(),
6441 CoderError,"SHOW is not implemented yet","`%s'",
6444 mng_info->show_warning++;
6447 if (memcmp(type,mng_sBIT,4) == 0)
6450 mng_info->have_global_sbit=MagickFalse;
6454 mng_info->global_sbit.gray=p[0];
6455 mng_info->global_sbit.red=p[0];
6456 mng_info->global_sbit.green=p[1];
6457 mng_info->global_sbit.blue=p[2];
6458 mng_info->global_sbit.alpha=p[3];
6459 mng_info->have_global_sbit=MagickTrue;
6462 if (memcmp(type,mng_pHYs,4) == 0)
6466 mng_info->global_x_pixels_per_unit=
6467 (size_t) mng_get_long(p);
6468 mng_info->global_y_pixels_per_unit=
6469 (size_t) mng_get_long(&p[4]);
6470 mng_info->global_phys_unit_type=p[8];
6471 mng_info->have_global_phys=MagickTrue;
6475 mng_info->have_global_phys=MagickFalse;
6477 if (memcmp(type,mng_pHYg,4) == 0)
6479 if (mng_info->phyg_warning == 0)
6480 (void) ThrowMagickException(exception,GetMagickModule(),
6481 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6483 mng_info->phyg_warning++;
6485 if (memcmp(type,mng_BASI,4) == 0)
6487 skip_to_iend=MagickTrue;
6489 if (mng_info->basi_warning == 0)
6490 (void) ThrowMagickException(exception,GetMagickModule(),
6491 CoderError,"BASI is not implemented yet","`%s'",
6494 mng_info->basi_warning++;
6495 #ifdef MNG_BASI_SUPPORTED
6496 basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6497 (p[2] << 8) | p[3]);
6498 basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6499 (p[6] << 8) | p[7]);
6500 basi_color_type=p[8];
6501 basi_compression_method=p[9];
6502 basi_filter_type=p[10];
6503 basi_interlace_method=p[11];
6505 basi_red=(p[12] << 8) & p[13];
6511 basi_green=(p[14] << 8) & p[15];
6517 basi_blue=(p[16] << 8) & p[17];
6523 basi_alpha=(p[18] << 8) & p[19];
6527 if (basi_sample_depth == 16)
6534 basi_viewable=p[20];
6540 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6544 if (memcmp(type,mng_IHDR,4)
6545 #if defined(JNG_SUPPORTED)
6546 && memcmp(type,mng_JHDR,4)
6550 /* Not an IHDR or JHDR chunk */
6552 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6557 if (logging != MagickFalse)
6558 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6559 " Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6561 mng_info->exists[object_id]=MagickTrue;
6562 mng_info->viewable[object_id]=MagickTrue;
6564 if (mng_info->invisible[object_id])
6566 if (logging != MagickFalse)
6567 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6568 " Skipping invisible object");
6570 skip_to_iend=MagickTrue;
6571 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6574 #if defined(MNG_INSERT_LAYERS)
6577 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6578 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6581 image_width=(size_t) mng_get_long(p);
6582 image_height=(size_t) mng_get_long(&p[4]);
6584 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6587 Insert a transparent background layer behind the entire animation
6588 if it is not full screen.
6590 #if defined(MNG_INSERT_LAYERS)
6591 if (insert_layers && mng_type && first_mng_object)
6593 if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6594 (image_width < mng_info->mng_width) ||
6595 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6596 (image_height < mng_info->mng_height) ||
6597 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6599 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6602 Allocate next image structure.
6604 AcquireNextImage(image_info,image,exception);
6606 if (GetNextImageInList(image) == (Image *) NULL)
6607 return(DestroyImageList(image));
6609 image=SyncNextImageInList(image);
6611 mng_info->image=image;
6613 if (term_chunk_found)
6615 image->start_loop=MagickTrue;
6616 image->iterations=mng_iterations;
6617 term_chunk_found=MagickFalse;
6621 image->start_loop=MagickFalse;
6623 /* Make a background rectangle. */
6626 image->columns=mng_info->mng_width;
6627 image->rows=mng_info->mng_height;
6628 image->page.width=mng_info->mng_width;
6629 image->page.height=mng_info->mng_height;
6632 image->background_color=mng_background_color;
6633 (void) SetImageBackgroundColor(image,exception);
6634 if (logging != MagickFalse)
6635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6636 " Inserted transparent background layer, W=%.20g, H=%.20g",
6637 (double) mng_info->mng_width,(double) mng_info->mng_height);
6641 Insert a background layer behind the upcoming image if
6642 framing_mode is 3, and we haven't already inserted one.
6644 if (insert_layers && (mng_info->framing_mode == 3) &&
6645 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6646 (simplicity & 0x08)))
6648 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6651 Allocate next image structure.
6653 AcquireNextImage(image_info,image,exception);
6655 if (GetNextImageInList(image) == (Image *) NULL)
6656 return(DestroyImageList(image));
6658 image=SyncNextImageInList(image);
6661 mng_info->image=image;
6663 if (term_chunk_found)
6665 image->start_loop=MagickTrue;
6666 image->iterations=mng_iterations;
6667 term_chunk_found=MagickFalse;
6671 image->start_loop=MagickFalse;
6674 image->columns=subframe_width;
6675 image->rows=subframe_height;
6676 image->page.width=subframe_width;
6677 image->page.height=subframe_height;
6678 image->page.x=mng_info->clip.left;
6679 image->page.y=mng_info->clip.top;
6680 image->background_color=mng_background_color;
6681 image->alpha_trait=UndefinedPixelTrait;
6682 (void) SetImageBackgroundColor(image,exception);
6684 if (logging != MagickFalse)
6685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6686 " Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6687 (double) mng_info->clip.left,(double) mng_info->clip.right,
6688 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6690 #endif /* MNG_INSERT_LAYERS */
6691 first_mng_object=MagickFalse;
6693 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6696 Allocate next image structure.
6698 AcquireNextImage(image_info,image,exception);
6700 if (GetNextImageInList(image) == (Image *) NULL)
6701 return(DestroyImageList(image));
6703 image=SyncNextImageInList(image);
6705 mng_info->image=image;
6706 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6707 GetBlobSize(image));
6709 if (status == MagickFalse)
6712 if (term_chunk_found)
6714 image->start_loop=MagickTrue;
6715 term_chunk_found=MagickFalse;
6719 image->start_loop=MagickFalse;
6721 if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6723 image->delay=frame_delay;
6724 frame_delay=default_frame_delay;
6730 image->page.width=mng_info->mng_width;
6731 image->page.height=mng_info->mng_height;
6732 image->page.x=mng_info->x_off[object_id];
6733 image->page.y=mng_info->y_off[object_id];
6734 image->iterations=mng_iterations;
6737 Seek back to the beginning of the IHDR or JHDR chunk's length field.
6740 if (logging != MagickFalse)
6741 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6742 " Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6745 offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6748 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6751 mng_info->image=image;
6752 mng_info->mng_type=mng_type;
6753 mng_info->object_id=object_id;
6755 if (memcmp(type,mng_IHDR,4) == 0)
6756 image=ReadOnePNGImage(mng_info,image_info,exception);
6758 #if defined(JNG_SUPPORTED)
6760 image=ReadOneJNGImage(mng_info,image_info,exception);
6763 if (image == (Image *) NULL)
6765 if (logging != MagickFalse)
6766 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6767 "exit ReadJNGImage() with error");
6769 return((Image *) NULL);
6772 if (image->columns == 0 || image->rows == 0)
6774 (void) CloseBlob(image);
6775 return(DestroyImageList(image));
6778 mng_info->image=image;
6785 if (mng_info->magn_methx || mng_info->magn_methy)
6791 if (logging != MagickFalse)
6792 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6793 " Processing MNG MAGN chunk");
6795 if (mng_info->magn_methx == 1)
6797 magnified_width=mng_info->magn_ml;
6799 if (image->columns > 1)
6800 magnified_width += mng_info->magn_mr;
6802 if (image->columns > 2)
6803 magnified_width += (png_uint_32)
6804 ((image->columns-2)*(mng_info->magn_mx));
6809 magnified_width=(png_uint_32) image->columns;
6811 if (image->columns > 1)
6812 magnified_width += mng_info->magn_ml-1;
6814 if (image->columns > 2)
6815 magnified_width += mng_info->magn_mr-1;
6817 if (image->columns > 3)
6818 magnified_width += (png_uint_32)
6819 ((image->columns-3)*(mng_info->magn_mx-1));
6822 if (mng_info->magn_methy == 1)
6824 magnified_height=mng_info->magn_mt;
6826 if (image->rows > 1)
6827 magnified_height += mng_info->magn_mb;
6829 if (image->rows > 2)
6830 magnified_height += (png_uint_32)
6831 ((image->rows-2)*(mng_info->magn_my));
6836 magnified_height=(png_uint_32) image->rows;
6838 if (image->rows > 1)
6839 magnified_height += mng_info->magn_mt-1;
6841 if (image->rows > 2)
6842 magnified_height += mng_info->magn_mb-1;
6844 if (image->rows > 3)
6845 magnified_height += (png_uint_32)
6846 ((image->rows-3)*(mng_info->magn_my-1));
6849 if (magnified_height > image->rows ||
6850 magnified_width > image->columns)
6877 /* Allocate next image structure. */
6879 if (logging != MagickFalse)
6880 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6881 " Allocate magnified image");
6883 AcquireNextImage(image_info,image,exception);
6885 if (GetNextImageInList(image) == (Image *) NULL)
6886 return(DestroyImageList(image));
6888 large_image=SyncNextImageInList(image);
6890 large_image->columns=magnified_width;
6891 large_image->rows=magnified_height;
6893 magn_methx=mng_info->magn_methx;
6894 magn_methy=mng_info->magn_methy;
6896 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6897 #define QM unsigned short
6898 if (magn_methx != 1 || magn_methy != 1)
6901 Scale pixels to unsigned shorts to prevent
6902 overflow of intermediate values of interpolations
6904 for (y=0; y < (ssize_t) image->rows; y++)
6906 q=GetAuthenticPixels(image,0,y,image->columns,1,
6909 for (x=(ssize_t) image->columns-1; x >= 0; x--)
6911 SetPixelRed(image,ScaleQuantumToShort(
6912 GetPixelRed(image,q)),q);
6913 SetPixelGreen(image,ScaleQuantumToShort(
6914 GetPixelGreen(image,q)),q);
6915 SetPixelBlue(image,ScaleQuantumToShort(
6916 GetPixelBlue(image,q)),q);
6917 SetPixelAlpha(image,ScaleQuantumToShort(
6918 GetPixelAlpha(image,q)),q);
6919 q+=GetPixelChannels(image);
6922 if (SyncAuthenticPixels(image,exception) == MagickFalse)
6930 if (image->alpha_trait != UndefinedPixelTrait)
6931 (void) SetImageBackgroundColor(large_image,exception);
6935 large_image->background_color.alpha=OpaqueAlpha;
6936 (void) SetImageBackgroundColor(large_image,exception);
6938 if (magn_methx == 4)
6941 if (magn_methx == 5)
6944 if (magn_methy == 4)
6947 if (magn_methy == 5)
6951 /* magnify the rows into the right side of the large image */
6953 if (logging != MagickFalse)
6954 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6955 " Magnify the rows to %.20g",
6956 (double) large_image->rows);
6957 m=(ssize_t) mng_info->magn_mt;
6959 length=(size_t) GetPixelChannels(image)*image->columns;
6960 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6961 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6963 if ((prev == (Quantum *) NULL) ||
6964 (next == (Quantum *) NULL))
6966 image=DestroyImageList(image);
6967 ThrowReaderException(ResourceLimitError,
6968 "MemoryAllocationFailed");
6971 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6972 (void) CopyMagickMemory(next,n,length);
6974 for (y=0; y < (ssize_t) image->rows; y++)
6977 m=(ssize_t) mng_info->magn_mt;
6979 else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6980 m=(ssize_t) mng_info->magn_mb;
6982 else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6983 m=(ssize_t) mng_info->magn_mb;
6985 else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6989 m=(ssize_t) mng_info->magn_my;
6995 if (y < (ssize_t) image->rows-1)
6997 n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6999 (void) CopyMagickMemory(next,n,length);
7002 for (i=0; i < m; i++, yy++)
7007 assert(yy < (ssize_t) large_image->rows);
7010 q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7012 q+=(large_image->columns-image->columns)*
7013 GetPixelChannels(large_image);
7015 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7017 /* To do: get color as function of indexes[x] */
7019 if (image->storage_class == PseudoClass)
7024 if (magn_methy <= 1)
7026 /* replicate previous */
7027 SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7028 SetPixelGreen(large_image,GetPixelGreen(image,
7030 SetPixelBlue(large_image,GetPixelBlue(image,
7032 SetPixelAlpha(large_image,GetPixelAlpha(image,
7036 else if (magn_methy == 2 || magn_methy == 4)
7040 SetPixelRed(large_image,GetPixelRed(image,
7042 SetPixelGreen(large_image,GetPixelGreen(image,
7044 SetPixelBlue(large_image,GetPixelBlue(image,
7046 SetPixelAlpha(large_image,GetPixelAlpha(image,
7053 SetPixelRed(large_image,((QM) (((ssize_t)
7054 (2*i*(GetPixelRed(image,n)
7055 -GetPixelRed(image,pixels)+m))/
7057 +GetPixelRed(image,pixels)))),q);
7058 SetPixelGreen(large_image,((QM) (((ssize_t)
7059 (2*i*(GetPixelGreen(image,n)
7060 -GetPixelGreen(image,pixels)+m))/
7062 +GetPixelGreen(image,pixels)))),q);
7063 SetPixelBlue(large_image,((QM) (((ssize_t)
7064 (2*i*(GetPixelBlue(image,n)
7065 -GetPixelBlue(image,pixels)+m))/
7067 +GetPixelBlue(image,pixels)))),q);
7069 if (image->alpha_trait != UndefinedPixelTrait)
7070 SetPixelAlpha(large_image, ((QM) (((ssize_t)
7071 (2*i*(GetPixelAlpha(image,n)
7072 -GetPixelAlpha(image,pixels)+m))
7074 GetPixelAlpha(image,pixels)))),q);
7077 if (magn_methy == 4)
7079 /* Replicate nearest */
7080 if (i <= ((m+1) << 1))
7081 SetPixelAlpha(large_image,GetPixelAlpha(image,
7084 SetPixelAlpha(large_image,GetPixelAlpha(image,
7089 else /* if (magn_methy == 3 || magn_methy == 5) */
7091 /* Replicate nearest */
7092 if (i <= ((m+1) << 1))
7094 SetPixelRed(large_image,GetPixelRed(image,
7096 SetPixelGreen(large_image,GetPixelGreen(image,
7098 SetPixelBlue(large_image,GetPixelBlue(image,
7100 SetPixelAlpha(large_image,GetPixelAlpha(image,
7106 SetPixelRed(large_image,GetPixelRed(image,n),q);
7107 SetPixelGreen(large_image,GetPixelGreen(image,n),
7109 SetPixelBlue(large_image,GetPixelBlue(image,n),
7111 SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7115 if (magn_methy == 5)
7117 SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7118 (GetPixelAlpha(image,n)
7119 -GetPixelAlpha(image,pixels))
7120 +m))/((ssize_t) (m*2))
7121 +GetPixelAlpha(image,pixels)),q);
7124 n+=GetPixelChannels(image);
7125 q+=GetPixelChannels(large_image);
7126 pixels+=GetPixelChannels(image);
7129 if (SyncAuthenticPixels(large_image,exception) == 0)
7135 prev=(Quantum *) RelinquishMagickMemory(prev);
7136 next=(Quantum *) RelinquishMagickMemory(next);
7138 length=image->columns;
7140 if (logging != MagickFalse)
7141 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7142 " Delete original image");
7144 DeleteImageFromList(&image);
7148 mng_info->image=image;
7150 /* magnify the columns */
7151 if (logging != MagickFalse)
7152 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7153 " Magnify the columns to %.20g",
7154 (double) image->columns);
7156 for (y=0; y < (ssize_t) image->rows; y++)
7161 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7162 pixels=q+(image->columns-length)*GetPixelChannels(image);
7163 n=pixels+GetPixelChannels(image);
7165 for (x=(ssize_t) (image->columns-length);
7166 x < (ssize_t) image->columns; x++)
7168 /* To do: Rewrite using Get/Set***PixelChannel() */
7170 if (x == (ssize_t) (image->columns-length))
7171 m=(ssize_t) mng_info->magn_ml;
7173 else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7174 m=(ssize_t) mng_info->magn_mr;
7176 else if (magn_methx <= 1 &&
7177 x == (ssize_t) image->columns-1)
7178 m=(ssize_t) mng_info->magn_mr;
7180 else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7184 m=(ssize_t) mng_info->magn_mx;
7186 for (i=0; i < m; i++)
7188 if (magn_methx <= 1)
7190 /* replicate previous */
7191 SetPixelRed(image,GetPixelRed(image,pixels),q);
7192 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7193 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7194 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7197 else if (magn_methx == 2 || magn_methx == 4)
7201 SetPixelRed(image,GetPixelRed(image,pixels),q);
7202 SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7203 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7204 SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7207 /* To do: Rewrite using Get/Set***PixelChannel() */
7211 SetPixelRed(image,(QM) ((2*i*(
7212 GetPixelRed(image,n)
7213 -GetPixelRed(image,pixels))+m)
7215 GetPixelRed(image,pixels)),q);
7217 SetPixelGreen(image,(QM) ((2*i*(
7218 GetPixelGreen(image,n)
7219 -GetPixelGreen(image,pixels))+m)
7221 GetPixelGreen(image,pixels)),q);
7223 SetPixelBlue(image,(QM) ((2*i*(
7224 GetPixelBlue(image,n)
7225 -GetPixelBlue(image,pixels))+m)
7227 GetPixelBlue(image,pixels)),q);
7228 if (image->alpha_trait != UndefinedPixelTrait)
7229 SetPixelAlpha(image,(QM) ((2*i*(
7230 GetPixelAlpha(image,n)
7231 -GetPixelAlpha(image,pixels))+m)
7233 GetPixelAlpha(image,pixels)),q);
7236 if (magn_methx == 4)
7238 /* Replicate nearest */
7239 if (i <= ((m+1) << 1))
7241 SetPixelAlpha(image,
7242 GetPixelAlpha(image,pixels)+0,q);
7246 SetPixelAlpha(image,
7247 GetPixelAlpha(image,n)+0,q);
7252 else /* if (magn_methx == 3 || magn_methx == 5) */
7254 /* Replicate nearest */
7255 if (i <= ((m+1) << 1))
7257 SetPixelRed(image,GetPixelRed(image,pixels),q);
7258 SetPixelGreen(image,GetPixelGreen(image,
7260 SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7261 SetPixelAlpha(image,GetPixelAlpha(image,
7267 SetPixelRed(image,GetPixelRed(image,n),q);
7268 SetPixelGreen(image,GetPixelGreen(image,n),q);
7269 SetPixelBlue(image,GetPixelBlue(image,n),q);
7270 SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7273 if (magn_methx == 5)
7276 SetPixelAlpha(image,
7277 (QM) ((2*i*( GetPixelAlpha(image,n)
7278 -GetPixelAlpha(image,pixels))+m)/
7280 +GetPixelAlpha(image,pixels)),q);
7283 q+=GetPixelChannels(image);
7285 n+=GetPixelChannels(image);
7288 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7291 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7292 if (magn_methx != 1 || magn_methy != 1)
7295 Rescale pixels to Quantum
7297 for (y=0; y < (ssize_t) image->rows; y++)
7299 q=GetAuthenticPixels(image,0,y,image->columns,1,
7302 for (x=(ssize_t) image->columns-1; x >= 0; x--)
7304 SetPixelRed(image,ScaleShortToQuantum(
7305 GetPixelRed(image,q)),q);
7306 SetPixelGreen(image,ScaleShortToQuantum(
7307 GetPixelGreen(image,q)),q);
7308 SetPixelBlue(image,ScaleShortToQuantum(
7309 GetPixelBlue(image,q)),q);
7310 SetPixelAlpha(image,ScaleShortToQuantum(
7311 GetPixelAlpha(image,q)),q);
7312 q+=GetPixelChannels(image);
7315 if (SyncAuthenticPixels(image,exception) == MagickFalse)
7320 if (logging != MagickFalse)
7321 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7322 " Finished MAGN processing");
7327 Crop_box is with respect to the upper left corner of the MNG.
7329 crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7330 crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7331 crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7332 crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7333 crop_box=mng_minimum_box(crop_box,mng_info->clip);
7334 crop_box=mng_minimum_box(crop_box,mng_info->frame);
7335 crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7336 if ((crop_box.left != (mng_info->image_box.left
7337 +mng_info->x_off[object_id])) ||
7338 (crop_box.right != (mng_info->image_box.right
7339 +mng_info->x_off[object_id])) ||
7340 (crop_box.top != (mng_info->image_box.top
7341 +mng_info->y_off[object_id])) ||
7342 (crop_box.bottom != (mng_info->image_box.bottom
7343 +mng_info->y_off[object_id])))
7345 if (logging != MagickFalse)
7346 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7347 " Crop the PNG image");
7349 if ((crop_box.left < crop_box.right) &&
7350 (crop_box.top < crop_box.bottom))
7359 Crop_info is with respect to the upper left corner of
7362 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7363 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7364 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7365 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7366 image->page.width=image->columns;
7367 image->page.height=image->rows;
7370 im=CropImage(image,&crop_info,exception);
7372 if (im != (Image *) NULL)
7374 image->columns=im->columns;
7375 image->rows=im->rows;
7376 im=DestroyImage(im);
7377 image->page.width=image->columns;
7378 image->page.height=image->rows;
7379 image->page.x=crop_box.left;
7380 image->page.y=crop_box.top;
7387 No pixels in crop area. The MNG spec still requires
7388 a layer, though, so make a single transparent pixel in
7389 the top left corner.
7394 (void) SetImageBackgroundColor(image,exception);
7395 image->page.width=1;
7396 image->page.height=1;
7401 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7402 image=mng_info->image;
7406 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7407 /* PNG does not handle depths greater than 16 so reduce it even
7410 if (image->depth > 16)
7414 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7415 if (image->depth > 8)
7417 /* To do: fill low byte properly */
7421 if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7425 if (image_info->number_scenes != 0)
7427 if (mng_info->scenes_found >
7428 (ssize_t) (image_info->first_scene+image_info->number_scenes))
7432 if (logging != MagickFalse)
7433 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7434 " Finished reading image datastream.");
7436 } while (LocaleCompare(image_info->magick,"MNG") == 0);
7438 (void) CloseBlob(image);
7440 if (logging != MagickFalse)
7441 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7442 " Finished reading all image datastreams.");
7444 #if defined(MNG_INSERT_LAYERS)
7445 if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7446 (mng_info->mng_height))
7449 Insert a background layer if nothing else was found.
7451 if (logging != MagickFalse)
7452 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7453 " No images found. Inserting a background layer.");
7455 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7458 Allocate next image structure.
7460 AcquireNextImage(image_info,image,exception);
7461 if (GetNextImageInList(image) == (Image *) NULL)
7463 if (logging != MagickFalse)
7464 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7465 " Allocation failed, returning NULL.");
7467 return(DestroyImageList(image));;
7469 image=SyncNextImageInList(image);
7471 image->columns=mng_info->mng_width;
7472 image->rows=mng_info->mng_height;
7473 image->page.width=mng_info->mng_width;
7474 image->page.height=mng_info->mng_height;
7477 image->background_color=mng_background_color;
7478 image->alpha_trait=UndefinedPixelTrait;
7480 if (image_info->ping == MagickFalse)
7481 (void) SetImageBackgroundColor(image,exception);
7483 mng_info->image_found++;
7486 image->iterations=mng_iterations;
7488 if (mng_iterations == 1)
7489 image->start_loop=MagickTrue;
7491 while (GetPreviousImageInList(image) != (Image *) NULL)
7494 if (image_count > 10*mng_info->image_found)
7496 if (logging != MagickFalse)
7497 (void) LogMagickEvent(CoderEvent,GetMagickModule()," No beginning");
7499 (void) ThrowMagickException(exception,GetMagickModule(),
7500 CoderError,"Linked list is corrupted, beginning of list not found",
7501 "`%s'",image_info->filename);
7503 return(DestroyImageList(image));
7506 image=GetPreviousImageInList(image);
7508 if (GetNextImageInList(image) == (Image *) NULL)
7510 if (logging != MagickFalse)
7511 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Corrupt list");
7513 (void) ThrowMagickException(exception,GetMagickModule(),
7514 CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7515 image_info->filename);
7519 if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7520 GetNextImageInList(image) ==
7523 if (logging != MagickFalse)
7524 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7525 " First image null");
7527 (void) ThrowMagickException(exception,GetMagickModule(),
7528 CoderError,"image->next for first image is NULL but shouldn't be.",
7529 "`%s'",image_info->filename);
7532 if (mng_info->image_found == 0)
7534 if (logging != MagickFalse)
7535 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7536 " No visible images found.");
7538 (void) ThrowMagickException(exception,GetMagickModule(),
7539 CoderError,"No visible images in file","`%s'",image_info->filename);
7541 return(DestroyImageList(image));
7544 if (mng_info->ticks_per_second)
7545 final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7546 final_delay/mng_info->ticks_per_second;
7549 image->start_loop=MagickTrue;
7551 /* Find final nonzero image delay */
7552 final_image_delay=0;
7554 while (GetNextImageInList(image) != (Image *) NULL)
7557 final_image_delay=image->delay;
7559 image=GetNextImageInList(image);
7562 if (final_delay < final_image_delay)
7563 final_delay=final_image_delay;
7565 image->delay=final_delay;
7567 if (logging != MagickFalse)
7568 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7569 " image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7570 (double) final_delay);
7572 if (logging != MagickFalse)
7578 image=GetFirstImageInList(image);
7580 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7581 " Before coalesce:");
7583 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7584 " scene 0 delay=%.20g",(double) image->delay);
7586 while (GetNextImageInList(image) != (Image *) NULL)
7588 image=GetNextImageInList(image);
7589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7590 " scene %.20g delay=%.20g",(double) scene++,
7591 (double) image->delay);
7595 image=GetFirstImageInList(image);
7596 #ifdef MNG_COALESCE_LAYERS
7606 if (logging != MagickFalse)
7607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7608 " Coalesce Images");
7611 next_image=CoalesceImages(image,exception);
7613 if (next_image == (Image *) NULL)
7614 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7616 image=DestroyImageList(image);
7619 for (next=image; next != (Image *) NULL; next=next_image)
7621 next->page.width=mng_info->mng_width;
7622 next->page.height=mng_info->mng_height;
7625 next->scene=scene++;
7626 next_image=GetNextImageInList(next);
7628 if (next_image == (Image *) NULL)
7631 if (next->delay == 0)
7634 next_image->previous=GetPreviousImageInList(next);
7635 if (GetPreviousImageInList(next) == (Image *) NULL)
7638 next->previous->next=next_image;
7639 next=DestroyImage(next);
7645 while (GetNextImageInList(image) != (Image *) NULL)
7646 image=GetNextImageInList(image);
7648 image->dispose=BackgroundDispose;
7650 if (logging != MagickFalse)
7656 image=GetFirstImageInList(image);
7658 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7659 " After coalesce:");
7661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7662 " scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7663 (double) image->dispose);
7665 while (GetNextImageInList(image) != (Image *) NULL)
7667 image=GetNextImageInList(image);
7669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7670 " scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7671 (double) image->delay,(double) image->dispose);
7675 if (logging != MagickFalse)
7676 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7677 " exit ReadOneMNGImage();");
7682 static Image *ReadMNGImage(const ImageInfo *image_info,
7683 ExceptionInfo *exception)
7695 /* Open image file. */
7697 assert(image_info != (const ImageInfo *) NULL);
7698 assert(image_info->signature == MagickCoreSignature);
7699 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7700 image_info->filename);
7701 assert(exception != (ExceptionInfo *) NULL);
7702 assert(exception->signature == MagickCoreSignature);
7703 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7704 image=AcquireImage(image_info,exception);
7705 mng_info=(MngInfo *) NULL;
7706 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7708 if (status == MagickFalse)
7709 return((Image *) NULL);
7711 /* Allocate a MngInfo structure. */
7713 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7715 if (mng_info == (MngInfo *) NULL)
7716 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7718 /* Initialize members of the MngInfo structure. */
7720 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7721 mng_info->image=image;
7722 image=ReadOneMNGImage(mng_info,image_info,exception);
7723 mng_info=MngInfoFreeStruct(mng_info);
7725 if (image == (Image *) NULL)
7727 if (logging != MagickFalse)
7728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7729 "exit ReadMNGImage() with error");
7731 return((Image *) NULL);
7733 (void) CloseBlob(image);
7735 if (logging != MagickFalse)
7736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7738 return(GetFirstImageInList(image));
7740 #else /* PNG_LIBPNG_VER > 10011 */
7741 static Image *ReadPNGImage(const ImageInfo *image_info,
7742 ExceptionInfo *exception)
7744 printf("Your PNG library is too old: You have libpng-%s\n",
7745 PNG_LIBPNG_VER_STRING);
7747 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7748 "PNG library is too old","`%s'",image_info->filename);
7750 return(Image *) NULL;
7753 static Image *ReadMNGImage(const ImageInfo *image_info,
7754 ExceptionInfo *exception)
7756 return(ReadPNGImage(image_info,exception));
7758 #endif /* PNG_LIBPNG_VER > 10011 */
7762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7766 % R e g i s t e r P N G I m a g e %
7770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7772 % RegisterPNGImage() adds properties for the PNG image format to
7773 % the list of supported formats. The properties include the image format
7774 % tag, a method to read and/or write the format, whether the format
7775 % supports the saving of more than one frame to the same file or blob,
7776 % whether the format supports native in-memory I/O, and a brief
7777 % description of the format.
7779 % The format of the RegisterPNGImage method is:
7781 % size_t RegisterPNGImage(void)
7784 ModuleExport size_t RegisterPNGImage(void)
7787 version[MagickPathExtent];
7795 "See http://www.libpng.org/ for details about the PNG format."
7800 "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7806 "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7812 #if defined(PNG_LIBPNG_VER_STRING)
7813 (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7814 (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7817 if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7819 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7820 (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7825 entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7826 entry->flags|=CoderSeekableStreamFlag; /* To do: eliminate this. */
7828 #if defined(MAGICKCORE_PNG_DELEGATE)
7829 entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7830 entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7833 entry->magick=(IsImageFormatHandler *) IsMNG;
7835 if (*version != '\0')
7836 entry->version=ConstantString(version);
7838 entry->mime_type=ConstantString("video/x-mng");
7839 entry->note=ConstantString(MNGNote);
7840 (void) RegisterMagickInfo(entry);
7842 entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7844 #if defined(MAGICKCORE_PNG_DELEGATE)
7845 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7846 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7849 entry->magick=(IsImageFormatHandler *) IsPNG;
7850 entry->flags^=CoderAdjoinFlag;
7851 entry->mime_type=ConstantString("image/png");
7853 if (*version != '\0')
7854 entry->version=ConstantString(version);
7856 entry->note=ConstantString(PNGNote);
7857 (void) RegisterMagickInfo(entry);
7859 entry=AcquireMagickInfo("PNG","PNG8",
7860 "8-bit indexed with optional binary transparency");
7862 #if defined(MAGICKCORE_PNG_DELEGATE)
7863 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7864 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7867 entry->magick=(IsImageFormatHandler *) IsPNG;
7868 entry->flags^=CoderAdjoinFlag;
7869 entry->mime_type=ConstantString("image/png");
7870 (void) RegisterMagickInfo(entry);
7872 entry=AcquireMagickInfo("PNG","PNG24",
7873 "opaque or binary transparent 24-bit RGB");
7876 #if defined(ZLIB_VERSION)
7877 (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7878 (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7880 if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7882 (void) ConcatenateMagickString(version,",",MagickPathExtent);
7883 (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7887 if (*version != '\0')
7888 entry->version=ConstantString(version);
7890 #if defined(MAGICKCORE_PNG_DELEGATE)
7891 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7892 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7895 entry->magick=(IsImageFormatHandler *) IsPNG;
7896 entry->flags^=CoderAdjoinFlag;
7897 entry->mime_type=ConstantString("image/png");
7898 (void) RegisterMagickInfo(entry);
7900 entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7902 #if defined(MAGICKCORE_PNG_DELEGATE)
7903 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7904 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7907 entry->magick=(IsImageFormatHandler *) IsPNG;
7908 entry->flags^=CoderAdjoinFlag;
7909 entry->mime_type=ConstantString("image/png");
7910 (void) RegisterMagickInfo(entry);
7912 entry=AcquireMagickInfo("PNG","PNG48",
7913 "opaque or binary transparent 48-bit RGB");
7915 #if defined(MAGICKCORE_PNG_DELEGATE)
7916 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7917 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7920 entry->magick=(IsImageFormatHandler *) IsPNG;
7921 entry->flags^=CoderAdjoinFlag;
7922 entry->mime_type=ConstantString("image/png");
7923 (void) RegisterMagickInfo(entry);
7925 entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7927 #if defined(MAGICKCORE_PNG_DELEGATE)
7928 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7929 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7932 entry->magick=(IsImageFormatHandler *) IsPNG;
7933 entry->flags^=CoderAdjoinFlag;
7934 entry->mime_type=ConstantString("image/png");
7935 (void) RegisterMagickInfo(entry);
7937 entry=AcquireMagickInfo("PNG","PNG00",
7938 "PNG inheriting bit-depth, color-type from original, if possible");
7940 #if defined(MAGICKCORE_PNG_DELEGATE)
7941 entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7942 entry->encoder=(EncodeImageHandler *) WritePNGImage;
7945 entry->magick=(IsImageFormatHandler *) IsPNG;
7946 entry->flags^=CoderAdjoinFlag;
7947 entry->mime_type=ConstantString("image/png");
7948 (void) RegisterMagickInfo(entry);
7950 entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7952 #if defined(JNG_SUPPORTED)
7953 #if defined(MAGICKCORE_PNG_DELEGATE)
7954 entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7955 entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7959 entry->magick=(IsImageFormatHandler *) IsJNG;
7960 entry->flags^=CoderAdjoinFlag;
7961 entry->mime_type=ConstantString("image/x-jng");
7962 entry->note=ConstantString(JNGNote);
7963 (void) RegisterMagickInfo(entry);
7965 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7966 ping_semaphore=AcquireSemaphoreInfo();
7969 return(MagickImageCoderSignature);
7973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7977 % U n r e g i s t e r P N G I m a g e %
7981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7983 % UnregisterPNGImage() removes format registrations made by the
7984 % PNG module from the list of supported formats.
7986 % The format of the UnregisterPNGImage method is:
7988 % UnregisterPNGImage(void)
7991 ModuleExport void UnregisterPNGImage(void)
7993 (void) UnregisterMagickInfo("MNG");
7994 (void) UnregisterMagickInfo("PNG");
7995 (void) UnregisterMagickInfo("PNG8");
7996 (void) UnregisterMagickInfo("PNG24");
7997 (void) UnregisterMagickInfo("PNG32");
7998 (void) UnregisterMagickInfo("PNG48");
7999 (void) UnregisterMagickInfo("PNG64");
8000 (void) UnregisterMagickInfo("PNG00");
8001 (void) UnregisterMagickInfo("JNG");
8003 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8004 if (ping_semaphore != (SemaphoreInfo *) NULL)
8005 RelinquishSemaphoreInfo(&ping_semaphore);
8009 #if defined(MAGICKCORE_PNG_DELEGATE)
8010 #if PNG_LIBPNG_VER > 10011
8012 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8016 % W r i t e M N G I m a g e %
8020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8022 % WriteMNGImage() writes an image in the Portable Network Graphics
8023 % Group's "Multiple-image Network Graphics" encoded image format.
8025 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
8027 % The format of the WriteMNGImage method is:
8029 % MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8030 % Image *image,ExceptionInfo *exception)
8032 % A description of each parameter follows.
8034 % o image_info: the image info.
8036 % o image: The image.
8038 % o exception: return any errors or warnings in this structure.
8040 % To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8041 % "To do" under ReadPNGImage):
8043 % Preserve all unknown and not-yet-handled known chunks found in input
8044 % PNG file and copy them into output PNG files according to the PNG
8047 % Write the iCCP chunk at MNG level when (icc profile length > 0)
8049 % Improve selection of color type (use indexed-colour or indexed-colour
8050 % with tRNS when 256 or fewer unique RGBA values are present).
8052 % Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8053 % This will be complicated if we limit ourselves to generating MNG-LC
8054 % files. For now we ignore disposal method 3 and simply overlay the next
8057 % Check for identical PLTE's or PLTE/tRNS combinations and use a
8058 % global MNG PLTE or PLTE/tRNS combination when appropriate.
8059 % [mostly done 15 June 1999 but still need to take care of tRNS]
8061 % Check for identical sRGB and replace with a global sRGB (and remove
8062 % gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8063 % replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8064 % local gAMA/cHRM with local sRGB if appropriate).
8066 % Check for identical sBIT chunks and write global ones.
8068 % Provide option to skip writing the signature tEXt chunks.
8070 % Use signatures to detect identical objects and reuse the first
8071 % instance of such objects instead of writing duplicate objects.
8073 % Use a smaller-than-32k value of compression window size when
8076 % Encode JNG datastreams. Mostly done as of 5.5.2; need to write
8077 % ancillary text chunks and save profiles.
8079 % Provide an option to force LC files (to ensure exact framing rate)
8082 % Provide an option to force VLC files instead of LC, even when offsets
8083 % are present. This will involve expanding the embedded images with a
8084 % transparent region at the top and/or left.
8088 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8089 png_info *ping_info, unsigned char *profile_type, unsigned char
8090 *profile_description, unsigned char *profile_data, png_uint_32 length)
8109 hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8111 if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8114 if (image_info->verbose)
8116 (void) printf("writing raw profile: type=%s, length=%.20g\n",
8117 (char *) profile_type, (double) length);
8120 #if PNG_LIBPNG_VER >= 10400
8121 text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8123 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8125 description_length=(png_uint_32) strlen((const char *) profile_description);
8126 allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8127 + description_length);
8128 #if PNG_LIBPNG_VER >= 10400
8129 text[0].text=(png_charp) png_malloc(ping,
8130 (png_alloc_size_t) allocated_length);
8131 text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8133 text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8134 text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8136 text[0].key[0]='\0';
8137 (void) ConcatenateMagickString(text[0].key,
8138 "Raw profile type ",MagickPathExtent);
8139 (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8143 (void) CopyMagickString(dp,(const char *) profile_description,
8145 dp+=description_length;
8147 (void) FormatLocaleString(dp,allocated_length-
8148 (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8151 for (i=0; i < (ssize_t) length; i++)
8155 *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8156 *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8161 text[0].text_length=(png_size_t) (dp-text[0].text);
8162 text[0].compression=image_info->compression == NoCompression ||
8163 (image_info->compression == UndefinedCompression &&
8164 text[0].text_length < 128) ? -1 : 0;
8166 if (text[0].text_length <= allocated_length)
8167 png_set_text(ping,ping_info,text,1);
8169 png_free(ping,text[0].text);
8170 png_free(ping,text[0].key);
8171 png_free(ping,text);
8174 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
8175 const char *string, MagickBooleanType logging)
8188 ResetImageProfileIterator(image);
8190 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8192 profile=GetImageProfile(image,name);
8194 if (profile != (const StringInfo *) NULL)
8199 if (LocaleNCompare(name,string,11) == 0)
8201 if (logging != MagickFalse)
8202 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8203 " Found %s profile",name);
8205 ping_profile=CloneStringInfo(profile);
8206 data=GetStringInfoDatum(ping_profile),
8207 length=(png_uint_32) GetStringInfoLength(ping_profile);
8212 (void) WriteBlobMSBULong(image,length-5); /* data length */
8213 (void) WriteBlob(image,length-1,data+1);
8214 (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
8215 ping_profile=DestroyStringInfo(ping_profile);
8219 name=GetNextImageProfile(image);
8225 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8226 const Quantum *p, const PixelInfo *q)
8231 value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8232 if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8233 return(MagickFalse);
8234 value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8235 if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8236 return(MagickFalse);
8237 value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8238 if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8239 return(MagickFalse);
8244 #if defined(PNG_tIME_SUPPORTED)
8245 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8246 const char *date,ExceptionInfo *exception)
8262 if (date != (const char *) NULL)
8264 if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
8267 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8268 "Invalid date format specified for png:tIME","`%s'",
8272 ptime.year=(png_uint_16) year;
8273 ptime.month=(png_byte) month;
8274 ptime.day=(png_byte) day;
8275 ptime.hour=(png_byte) hour;
8276 ptime.minute=(png_byte) minute;
8277 ptime.second=(png_byte) second;
8282 png_convert_from_time_t(&ptime,ttime);
8284 png_set_tIME(ping,info,&ptime);
8288 /* Write one PNG image */
8289 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8290 const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8322 ping_trans_alpha[256];
8350 ping_have_cheap_transparency,
8364 /* ping_exclude_EXIF, */
8368 /* ping_exclude_iTXt, */
8374 /* ping_exclude_tRNS, */
8377 ping_exclude_zCCP, /* hex-encoded iCCP */
8380 ping_preserve_colormap,
8382 ping_need_colortype_warning,
8390 *volatile pixel_info;
8409 ping_interlace_method,
8410 ping_compression_method,
8427 number_semitransparent,
8429 ping_pHYs_unit_type;
8432 ping_pHYs_x_resolution,
8433 ping_pHYs_y_resolution;
8435 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8436 " Enter WriteOnePNGImage()");
8438 image = CloneImage(IMimage,0,0,MagickFalse,exception);
8439 image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8440 if (image_info == (ImageInfo *) NULL)
8441 ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8443 /* Define these outside of the following "if logging()" block so they will
8444 * show in debuggers.
8447 (void) ConcatenateMagickString(im_vers,
8448 MagickLibVersionText,MagickPathExtent);
8449 (void) ConcatenateMagickString(im_vers,
8450 MagickLibAddendum,MagickPathExtent);
8453 (void) ConcatenateMagickString(libpng_vers,
8454 PNG_LIBPNG_VER_STRING,32);
8456 (void) ConcatenateMagickString(libpng_runv,
8457 png_get_libpng_ver(NULL),32);
8460 (void) ConcatenateMagickString(zlib_vers,
8463 (void) ConcatenateMagickString(zlib_runv,
8466 if (logging != MagickFalse)
8468 (void) LogMagickEvent(CoderEvent,GetMagickModule()," IM version = %s",
8470 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Libpng version = %s",
8472 if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8474 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8477 (void) LogMagickEvent(CoderEvent,GetMagickModule()," Zlib version = %s",
8479 if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8481 (void) LogMagickEvent(CoderEvent,GetMagickModule()," running with %s",
8486 /* Initialize some stuff */
8489 ping_interlace_method=0,
8490 ping_compression_method=0,
8491 ping_filter_method=0,
8494 ping_background.red = 0;
8495 ping_background.green = 0;
8496 ping_background.blue = 0;
8497 ping_background.gray = 0;
8498 ping_background.index = 0;
8500 ping_trans_color.red=0;
8501 ping_trans_color.green=0;
8502 ping_trans_color.blue=0;
8503 ping_trans_color.gray=0;
8505 ping_pHYs_unit_type = 0;
8506 ping_pHYs_x_resolution = 0;
8507 ping_pHYs_y_resolution = 0;
8509 ping_have_blob=MagickFalse;
8510 ping_have_cheap_transparency=MagickFalse;
8511 ping_have_color=MagickTrue;
8512 ping_have_non_bw=MagickTrue;
8513 ping_have_PLTE=MagickFalse;
8514 ping_have_bKGD=MagickFalse;
8515 ping_have_eXIf=MagickTrue;
8516 ping_have_iCCP=MagickFalse;
8517 ping_have_pHYs=MagickFalse;
8518 ping_have_sRGB=MagickFalse;
8519 ping_have_tRNS=MagickFalse;
8521 ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8522 ping_exclude_caNv=mng_info->ping_exclude_caNv;
8523 ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8524 ping_exclude_date=mng_info->ping_exclude_date;
8525 /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8526 ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8527 ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8528 ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8529 /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8530 ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8531 ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8532 ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8533 ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8534 ping_exclude_tIME=mng_info->ping_exclude_tIME;
8535 /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8536 ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8537 ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8538 ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8540 ping_preserve_colormap = mng_info->ping_preserve_colormap;
8541 ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8542 ping_need_colortype_warning = MagickFalse;
8544 /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8545 * i.e., eliminate the ICC profile and set image->rendering_intent.
8546 * Note that this will not involve any changes to the actual pixels
8547 * but merely passes information to applications that read the resulting
8550 * To do: recognize other variants of the sRGB profile, using the CRC to
8551 * verify all recognized variants including the 7 already known.
8553 * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8555 * Use something other than image->rendering_intent to record the fact
8556 * that the sRGB profile was found.
8558 * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8559 * profile. Record the Blackpoint Compensation, if any.
8561 if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8569 ResetImageProfileIterator(image);
8570 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8572 profile=GetImageProfile(image,name);
8574 if (profile != (StringInfo *) NULL)
8576 if ((LocaleCompare(name,"ICC") == 0) ||
8577 (LocaleCompare(name,"ICM") == 0))
8592 length=(png_uint_32) GetStringInfoLength(profile);
8594 for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8596 if (length == sRGB_info[icheck].len)
8600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8601 " Got a %lu-byte ICC profile (potentially sRGB)",
8602 (unsigned long) length);
8604 data=GetStringInfoDatum(profile);
8605 profile_crc=crc32(0,data,length);
8607 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8608 " with crc=%8x",(unsigned int) profile_crc);
8612 if (profile_crc == sRGB_info[icheck].crc)
8614 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8615 " It is sRGB with rendering intent = %s",
8616 Magick_RenderingIntentString_from_PNG_RenderingIntent(
8617 sRGB_info[icheck].intent));
8618 if (image->rendering_intent==UndefinedIntent)
8620 image->rendering_intent=
8621 Magick_RenderingIntent_from_PNG_RenderingIntent(
8622 sRGB_info[icheck].intent);
8624 ping_exclude_iCCP = MagickTrue;
8625 ping_exclude_zCCP = MagickTrue;
8626 ping_have_sRGB = MagickTrue;
8631 if (sRGB_info[icheck].len == 0)
8632 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8633 " Got %lu-byte ICC profile not recognized as sRGB",
8634 (unsigned long) length);
8637 name=GetNextImageProfile(image);
8642 number_semitransparent = 0;
8643 number_transparent = 0;
8645 if (logging != MagickFalse)
8647 if (image->storage_class == UndefinedClass)
8648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8649 " image->storage_class=UndefinedClass");
8650 if (image->storage_class == DirectClass)
8651 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8652 " image->storage_class=DirectClass");
8653 if (image->storage_class == PseudoClass)
8654 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8655 " image->storage_class=PseudoClass");
8656 (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8657 " image->taint=MagickTrue":
8658 " image->taint=MagickFalse");
8661 if (image->storage_class == PseudoClass &&
8662 (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8663 mng_info->write_png48 || mng_info->write_png64 ||
8664 (mng_info->write_png_colortype != 1 &&
8665 mng_info->write_png_colortype != 5)))
8667 (void) SyncImage(image,exception);
8668 image->storage_class = DirectClass;
8671 if (ping_preserve_colormap == MagickFalse)
8673 if (image->storage_class != PseudoClass && image->colormap != NULL)
8675 /* Free the bogus colormap; it can cause trouble later */
8676 if (logging != MagickFalse)
8677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8678 " Freeing bogus colormap");
8679 (void) RelinquishMagickMemory(image->colormap);
8680 image->colormap=NULL;
8684 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8685 (void) TransformImageColorspace(image,sRGBColorspace,exception);
8688 Sometimes we get PseudoClass images whose RGB values don't match
8689 the colors in the colormap. This code syncs the RGB values.
8691 if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8692 (void) SyncImage(image,exception);
8694 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8695 if (image->depth > 8)
8697 if (logging != MagickFalse)
8698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8699 " Reducing PNG bit depth to 8 since this is a Q8 build.");
8705 /* Respect the -depth option */
8706 if (image->depth < 4)
8711 if (image->depth > 2)
8713 /* Scale to 4-bit */
8714 LBR04PacketRGBO(image->background_color);
8716 for (y=0; y < (ssize_t) image->rows; y++)
8718 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8720 if (r == (Quantum *) NULL)
8723 for (x=0; x < (ssize_t) image->columns; x++)
8726 r+=GetPixelChannels(image);
8729 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8733 if (image->storage_class == PseudoClass && image->colormap != NULL)
8735 for (i=0; i < (ssize_t) image->colors; i++)
8737 LBR04PacketRGBO(image->colormap[i]);
8741 else if (image->depth > 1)
8743 /* Scale to 2-bit */
8744 LBR02PacketRGBO(image->background_color);
8746 for (y=0; y < (ssize_t) image->rows; y++)
8748 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8750 if (r == (Quantum *) NULL)
8753 for (x=0; x < (ssize_t) image->columns; x++)
8756 r+=GetPixelChannels(image);
8759 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8763 if (image->storage_class == PseudoClass && image->colormap != NULL)
8765 for (i=0; i < (ssize_t) image->colors; i++)
8767 LBR02PacketRGBO(image->colormap[i]);
8773 /* Scale to 1-bit */
8774 LBR01PacketRGBO(image->background_color);
8776 for (y=0; y < (ssize_t) image->rows; y++)
8778 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8780 if (r == (Quantum *) NULL)
8783 for (x=0; x < (ssize_t) image->columns; x++)
8786 r+=GetPixelChannels(image);
8789 if (SyncAuthenticPixels(image,exception) == MagickFalse)
8793 if (image->storage_class == PseudoClass && image->colormap != NULL)
8795 for (i=0; i < (ssize_t) image->colors; i++)
8797 LBR01PacketRGBO(image->colormap[i]);
8803 /* To do: set to next higher multiple of 8 */
8804 if (image->depth < 8)
8807 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8808 /* PNG does not handle depths greater than 16 so reduce it even
8811 if (image->depth > 8)
8815 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8816 if (image->depth > 8)
8818 /* To do: fill low byte properly */
8822 if (image->depth == 16 && mng_info->write_png_depth != 16)
8823 if (mng_info->write_png8 ||
8824 LosslessReduceDepthOK(image,exception) != MagickFalse)
8828 image_colors = (int) image->colors;
8829 number_opaque = (int) image->colors;
8830 number_transparent = 0;
8831 number_semitransparent = 0;
8833 if (mng_info->write_png_colortype &&
8834 (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8835 mng_info->write_png_colortype < 4 &&
8836 image->alpha_trait == UndefinedPixelTrait)))
8838 /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8839 * are not going to need the result.
8841 if (mng_info->write_png_colortype == 1 ||
8842 mng_info->write_png_colortype == 5)
8843 ping_have_color=MagickFalse;
8845 if (image->alpha_trait != UndefinedPixelTrait)
8847 number_transparent = 2;
8848 number_semitransparent = 1;
8852 if (mng_info->write_png_colortype < 7)
8856 * Normally we run this just once, but in the case of writing PNG8
8857 * we reduce the transparency to binary and run again, then if there
8858 * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8859 * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8860 * palette. Then (To do) we take care of a final reduction that is only
8861 * needed if there are still 256 colors present and one of them has both
8862 * transparent and opaque instances.
8865 tried_332 = MagickFalse;
8866 tried_333 = MagickFalse;
8867 tried_444 = MagickFalse;
8872 * Sometimes we get DirectClass images that have 256 colors or fewer.
8873 * This code will build a colormap.
8875 * Also, sometimes we get PseudoClass images with an out-of-date
8876 * colormap. This code will replace the colormap with a new one.
8877 * Sometimes we get PseudoClass images that have more than 256 colors.
8878 * This code will delete the colormap and change the image to
8881 * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8882 * even though it sometimes contains left-over non-opaque values.
8884 * Also we gather some information (number of opaque, transparent,
8885 * and semitransparent pixels, and whether the image has any non-gray
8886 * pixels or only black-and-white pixels) that we might need later.
8888 * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8889 * we need to check for bogus non-opaque values, at least.
8897 semitransparent[260],
8900 register const Quantum
8907 if (logging != MagickFalse)
8908 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8909 " Enter BUILD_PALETTE:");
8911 if (logging != MagickFalse)
8913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8914 " image->columns=%.20g",(double) image->columns);
8915 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8916 " image->rows=%.20g",(double) image->rows);
8917 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8918 " image->alpha_trait=%.20g",(double) image->alpha_trait);
8919 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8920 " image->depth=%.20g",(double) image->depth);
8922 if (image->storage_class == PseudoClass && image->colormap != NULL)
8924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8925 " Original colormap:");
8926 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8927 " i (red,green,blue,alpha)");
8929 for (i=0; i < 256; i++)
8931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8932 " %d (%d,%d,%d,%d)",
8934 (int) image->colormap[i].red,
8935 (int) image->colormap[i].green,
8936 (int) image->colormap[i].blue,
8937 (int) image->colormap[i].alpha);
8940 for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8944 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8945 " %d (%d,%d,%d,%d)",
8947 (int) image->colormap[i].red,
8948 (int) image->colormap[i].green,
8949 (int) image->colormap[i].blue,
8950 (int) image->colormap[i].alpha);
8955 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8956 " image->colors=%d",(int) image->colors);
8958 if (image->colors == 0)
8959 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8960 " (zero means unknown)");
8962 if (ping_preserve_colormap == MagickFalse)
8963 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8964 " Regenerate the colormap");
8969 number_semitransparent = 0;
8970 number_transparent = 0;
8972 for (y=0; y < (ssize_t) image->rows; y++)
8974 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8976 if (q == (Quantum *) NULL)
8979 for (x=0; x < (ssize_t) image->columns; x++)
8981 if (image->alpha_trait == UndefinedPixelTrait ||
8982 GetPixelAlpha(image,q) == OpaqueAlpha)
8984 if (number_opaque < 259)
8986 if (number_opaque == 0)
8988 GetPixelInfoPixel(image, q, opaque);
8989 opaque[0].alpha=OpaqueAlpha;
8993 for (i=0; i< (ssize_t) number_opaque; i++)
8995 if (Magick_png_color_equal(image,q,opaque+i))
8999 if (i == (ssize_t) number_opaque && number_opaque < 259)
9002 GetPixelInfoPixel(image, q, opaque+i);
9003 opaque[i].alpha=OpaqueAlpha;
9007 else if (GetPixelAlpha(image,q) == TransparentAlpha)
9009 if (number_transparent < 259)
9011 if (number_transparent == 0)
9013 GetPixelInfoPixel(image, q, transparent);
9014 ping_trans_color.red=(unsigned short)
9015 GetPixelRed(image,q);
9016 ping_trans_color.green=(unsigned short)
9017 GetPixelGreen(image,q);
9018 ping_trans_color.blue=(unsigned short)
9019 GetPixelBlue(image,q);
9020 ping_trans_color.gray=(unsigned short)
9021 GetPixelGray(image,q);
9022 number_transparent = 1;
9025 for (i=0; i< (ssize_t) number_transparent; i++)
9027 if (Magick_png_color_equal(image,q,transparent+i))
9031 if (i == (ssize_t) number_transparent &&
9032 number_transparent < 259)
9034 number_transparent++;
9035 GetPixelInfoPixel(image,q,transparent+i);
9041 if (number_semitransparent < 259)
9043 if (number_semitransparent == 0)
9045 GetPixelInfoPixel(image,q,semitransparent);
9046 number_semitransparent = 1;
9049 for (i=0; i< (ssize_t) number_semitransparent; i++)
9051 if (Magick_png_color_equal(image,q,semitransparent+i)
9052 && GetPixelAlpha(image,q) ==
9053 semitransparent[i].alpha)
9057 if (i == (ssize_t) number_semitransparent &&
9058 number_semitransparent < 259)
9060 number_semitransparent++;
9061 GetPixelInfoPixel(image, q, semitransparent+i);
9065 q+=GetPixelChannels(image);
9069 if (mng_info->write_png8 == MagickFalse &&
9070 ping_exclude_bKGD == MagickFalse)
9072 /* Add the background color to the palette, if it
9073 * isn't already there.
9075 if (logging != MagickFalse)
9077 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9078 " Check colormap for background (%d,%d,%d)",
9079 (int) image->background_color.red,
9080 (int) image->background_color.green,
9081 (int) image->background_color.blue);
9083 for (i=0; i<number_opaque; i++)
9085 if (opaque[i].red == image->background_color.red &&
9086 opaque[i].green == image->background_color.green &&
9087 opaque[i].blue == image->background_color.blue)
9090 if (number_opaque < 259 && i == number_opaque)
9092 opaque[i] = image->background_color;
9093 ping_background.index = i;
9095 if (logging != MagickFalse)
9097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9098 " background_color index is %d",(int) i);
9102 else if (logging != MagickFalse)
9103 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9104 " No room in the colormap to add background color");
9107 image_colors=number_opaque+number_transparent+number_semitransparent;
9109 if (logging != MagickFalse)
9111 if (image_colors > 256)
9112 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9113 " image has more than 256 colors");
9116 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9117 " image has %d colors",image_colors);
9120 if (ping_preserve_colormap != MagickFalse)
9123 if (mng_info->write_png_colortype != 7) /* We won't need this info */
9125 ping_have_color=MagickFalse;
9126 ping_have_non_bw=MagickFalse;
9128 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9130 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9131 "incompatible colorspace");
9132 ping_have_color=MagickTrue;
9133 ping_have_non_bw=MagickTrue;
9136 if(image_colors > 256)
9138 for (y=0; y < (ssize_t) image->rows; y++)
9140 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9142 if (q == (Quantum *) NULL)
9146 for (x=0; x < (ssize_t) image->columns; x++)
9148 if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
9149 GetPixelRed(image,s) != GetPixelBlue(image,s))
9151 ping_have_color=MagickTrue;
9152 ping_have_non_bw=MagickTrue;
9155 s+=GetPixelChannels(image);
9158 if (ping_have_color != MagickFalse)
9161 /* Worst case is black-and-white; we are looking at every
9165 if (ping_have_non_bw == MagickFalse)
9168 for (x=0; x < (ssize_t) image->columns; x++)
9170 if (GetPixelRed(image,s) != 0 &&
9171 GetPixelRed(image,s) != QuantumRange)
9173 ping_have_non_bw=MagickTrue;
9176 s+=GetPixelChannels(image);
9183 if (image_colors < 257)
9189 * Initialize image colormap.
9192 if (logging != MagickFalse)
9193 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194 " Sort the new colormap");
9196 /* Sort palette, transparent first */;
9200 for (i=0; i<number_transparent; i++)
9201 colormap[n++] = transparent[i];
9203 for (i=0; i<number_semitransparent; i++)
9204 colormap[n++] = semitransparent[i];
9206 for (i=0; i<number_opaque; i++)
9207 colormap[n++] = opaque[i];
9209 ping_background.index +=
9210 (number_transparent + number_semitransparent);
9212 /* image_colors < 257; search the colormap instead of the pixels
9213 * to get ping_have_color and ping_have_non_bw
9217 if (ping_have_color == MagickFalse)
9219 if (colormap[i].red != colormap[i].green ||
9220 colormap[i].red != colormap[i].blue)
9222 ping_have_color=MagickTrue;
9223 ping_have_non_bw=MagickTrue;
9228 if (ping_have_non_bw == MagickFalse)
9230 if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9231 ping_have_non_bw=MagickTrue;
9235 if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9236 (number_transparent == 0 && number_semitransparent == 0)) &&
9237 (((mng_info->write_png_colortype-1) ==
9238 PNG_COLOR_TYPE_PALETTE) ||
9239 (mng_info->write_png_colortype == 0)))
9241 if (logging != MagickFalse)
9243 if (n != (ssize_t) image_colors)
9244 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9245 " image_colors (%d) and n (%d) don't match",
9248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9249 " AcquireImageColormap");
9252 image->colors = image_colors;
9254 if (AcquireImageColormap(image,image_colors,exception) ==
9256 ThrowWriterException(ResourceLimitError,
9257 "MemoryAllocationFailed");
9259 for (i=0; i< (ssize_t) image_colors; i++)
9260 image->colormap[i] = colormap[i];
9262 if (logging != MagickFalse)
9264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9265 " image->colors=%d (%d)",
9266 (int) image->colors, image_colors);
9268 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9269 " Update the pixel indexes");
9272 /* Sync the pixel indices with the new colormap */
9274 for (y=0; y < (ssize_t) image->rows; y++)
9276 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9278 if (q == (Quantum *) NULL)
9281 for (x=0; x < (ssize_t) image->columns; x++)
9283 for (i=0; i< (ssize_t) image_colors; i++)
9285 if ((image->alpha_trait == UndefinedPixelTrait ||
9286 image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9287 image->colormap[i].red == GetPixelRed(image,q) &&
9288 image->colormap[i].green == GetPixelGreen(image,q) &&
9289 image->colormap[i].blue == GetPixelBlue(image,q))
9291 SetPixelIndex(image,i,q);
9295 q+=GetPixelChannels(image);
9298 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9304 if (logging != MagickFalse)
9306 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9307 " image->colors=%d", (int) image->colors);
9309 if (image->colormap != NULL)
9311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9312 " i (red,green,blue,alpha)");
9314 for (i=0; i < (ssize_t) image->colors; i++)
9316 if (i < 300 || i >= (ssize_t) image->colors - 10)
9318 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9319 " %d (%d,%d,%d,%d)",
9321 (int) image->colormap[i].red,
9322 (int) image->colormap[i].green,
9323 (int) image->colormap[i].blue,
9324 (int) image->colormap[i].alpha);
9329 if (number_transparent < 257)
9330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331 " number_transparent = %d",
9332 number_transparent);
9335 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9336 " number_transparent > 256");
9338 if (number_opaque < 257)
9339 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9340 " number_opaque = %d",
9344 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9345 " number_opaque > 256");
9347 if (number_semitransparent < 257)
9348 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9349 " number_semitransparent = %d",
9350 number_semitransparent);
9353 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9354 " number_semitransparent > 256");
9356 if (ping_have_non_bw == MagickFalse)
9357 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9358 " All pixels and the background are black or white");
9360 else if (ping_have_color == MagickFalse)
9361 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9362 " All pixels and the background are gray");
9365 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9366 " At least one pixel or the background is non-gray");
9368 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9369 " Exit BUILD_PALETTE:");
9372 if (mng_info->write_png8 == MagickFalse)
9375 /* Make any reductions necessary for the PNG8 format */
9376 if (image_colors <= 256 &&
9377 image_colors != 0 && image->colormap != NULL &&
9378 number_semitransparent == 0 &&
9379 number_transparent <= 1)
9382 /* PNG8 can't have semitransparent colors so we threshold the
9383 * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9384 * transparent color so if more than one is transparent we merge
9385 * them into image->background_color.
9387 if (number_semitransparent != 0 || number_transparent > 1)
9389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9390 " Thresholding the alpha channel to binary");
9392 for (y=0; y < (ssize_t) image->rows; y++)
9394 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9396 if (r == (Quantum *) NULL)
9399 for (x=0; x < (ssize_t) image->columns; x++)
9401 if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9403 SetPixelViaPixelInfo(image,&image->background_color,r);
9404 SetPixelAlpha(image,TransparentAlpha,r);
9407 SetPixelAlpha(image,OpaqueAlpha,r);
9408 r+=GetPixelChannels(image);
9411 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9414 if (image_colors != 0 && image_colors <= 256 &&
9415 image->colormap != NULL)
9416 for (i=0; i<image_colors; i++)
9417 image->colormap[i].alpha =
9418 (image->colormap[i].alpha > TransparentAlpha/2 ?
9419 TransparentAlpha : OpaqueAlpha);
9424 /* PNG8 can't have more than 256 colors so we quantize the pixels and
9425 * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette. If the
9426 * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9429 if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9431 if (logging != MagickFalse)
9432 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9433 " Quantizing the background color to 4-4-4");
9435 tried_444 = MagickTrue;
9437 LBR04PacketRGB(image->background_color);
9439 if (logging != MagickFalse)
9440 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9441 " Quantizing the pixel colors to 4-4-4");
9443 if (image->colormap == NULL)
9445 for (y=0; y < (ssize_t) image->rows; y++)
9447 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9449 if (r == (Quantum *) NULL)
9452 for (x=0; x < (ssize_t) image->columns; x++)
9454 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9456 r+=GetPixelChannels(image);
9459 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9464 else /* Should not reach this; colormap already exists and
9467 if (logging != MagickFalse)
9468 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9469 " Quantizing the colormap to 4-4-4");
9471 for (i=0; i<image_colors; i++)
9473 LBR04PacketRGB(image->colormap[i]);
9479 if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9481 if (logging != MagickFalse)
9482 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9483 " Quantizing the background color to 3-3-3");
9485 tried_333 = MagickTrue;
9487 LBR03PacketRGB(image->background_color);
9489 if (logging != MagickFalse)
9490 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9491 " Quantizing the pixel colors to 3-3-3-1");
9493 if (image->colormap == NULL)
9495 for (y=0; y < (ssize_t) image->rows; y++)
9497 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9499 if (r == (Quantum *) NULL)
9502 for (x=0; x < (ssize_t) image->columns; x++)
9504 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9506 r+=GetPixelChannels(image);
9509 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9514 else /* Should not reach this; colormap already exists and
9517 if (logging != MagickFalse)
9518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9519 " Quantizing the colormap to 3-3-3-1");
9520 for (i=0; i<image_colors; i++)
9522 LBR03PacketRGB(image->colormap[i]);
9528 if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9530 if (logging != MagickFalse)
9531 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9532 " Quantizing the background color to 3-3-2");
9534 tried_332 = MagickTrue;
9536 /* Red and green were already done so we only quantize the blue
9540 LBR02PacketBlue(image->background_color);
9542 if (logging != MagickFalse)
9543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9544 " Quantizing the pixel colors to 3-3-2-1");
9546 if (image->colormap == NULL)
9548 for (y=0; y < (ssize_t) image->rows; y++)
9550 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9552 if (r == (Quantum *) NULL)
9555 for (x=0; x < (ssize_t) image->columns; x++)
9557 if (GetPixelAlpha(image,r) == OpaqueAlpha)
9559 r+=GetPixelChannels(image);
9562 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9567 else /* Should not reach this; colormap already exists and
9570 if (logging != MagickFalse)
9571 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9572 " Quantizing the colormap to 3-3-2-1");
9573 for (i=0; i<image_colors; i++)
9575 LBR02PacketBlue(image->colormap[i]);
9581 if (image_colors == 0 || image_colors > 256)
9583 /* Take care of special case with 256 opaque colors + 1 transparent
9584 * color. We don't need to quantize to 2-3-2-1; we only need to
9585 * eliminate one color, so we'll merge the two darkest red
9586 * colors (0x49, 0, 0) -> (0x24, 0, 0).
9588 if (logging != MagickFalse)
9589 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9590 " Merging two dark red background colors to 3-3-2-1");
9592 if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9593 ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9594 ScaleQuantumToChar(image->background_color.blue) == 0x00)
9596 image->background_color.red=ScaleCharToQuantum(0x24);
9599 if (logging != MagickFalse)
9600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9601 " Merging two dark red pixel colors to 3-3-2-1");
9603 if (image->colormap == NULL)
9605 for (y=0; y < (ssize_t) image->rows; y++)
9607 r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9609 if (r == (Quantum *) NULL)
9612 for (x=0; x < (ssize_t) image->columns; x++)
9614 if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9615 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9616 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9617 GetPixelAlpha(image,r) == OpaqueAlpha)
9619 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9621 r+=GetPixelChannels(image);
9624 if (SyncAuthenticPixels(image,exception) == MagickFalse)
9632 for (i=0; i<image_colors; i++)
9634 if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9635 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9636 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9638 image->colormap[i].red=ScaleCharToQuantum(0x24);
9645 /* END OF BUILD_PALETTE */
9647 /* If we are excluding the tRNS chunk and there is transparency,
9648 * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9651 if (mng_info->ping_exclude_tRNS != MagickFalse &&
9652 (number_transparent != 0 || number_semitransparent != 0))
9654 unsigned int colortype=mng_info->write_png_colortype;
9656 if (ping_have_color == MagickFalse)
9657 mng_info->write_png_colortype = 5;
9660 mng_info->write_png_colortype = 7;
9662 if (colortype != 0 &&
9663 mng_info->write_png_colortype != colortype)
9664 ping_need_colortype_warning=MagickTrue;
9668 /* See if cheap transparency is possible. It is only possible
9669 * when there is a single transparent color, no semitransparent
9670 * color, and no opaque color that has the same RGB components
9671 * as the transparent color. We only need this information if
9672 * we are writing a PNG with colortype 0 or 2, and we have not
9673 * excluded the tRNS chunk.
9675 if (number_transparent == 1 &&
9676 mng_info->write_png_colortype < 4)
9678 ping_have_cheap_transparency = MagickTrue;
9680 if (number_semitransparent != 0)
9681 ping_have_cheap_transparency = MagickFalse;
9683 else if (image_colors == 0 || image_colors > 256 ||
9684 image->colormap == NULL)
9686 register const Quantum
9689 for (y=0; y < (ssize_t) image->rows; y++)
9691 q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9693 if (q == (Quantum *) NULL)
9696 for (x=0; x < (ssize_t) image->columns; x++)
9698 if (GetPixelAlpha(image,q) != TransparentAlpha &&
9699 (unsigned short) GetPixelRed(image,q) ==
9700 ping_trans_color.red &&
9701 (unsigned short) GetPixelGreen(image,q) ==
9702 ping_trans_color.green &&
9703 (unsigned short) GetPixelBlue(image,q) ==
9704 ping_trans_color.blue)
9706 ping_have_cheap_transparency = MagickFalse;
9710 q+=GetPixelChannels(image);
9713 if (ping_have_cheap_transparency == MagickFalse)
9719 /* Assuming that image->colormap[0] is the one transparent color
9720 * and that all others are opaque.
9722 if (image_colors > 1)
9723 for (i=1; i<image_colors; i++)
9724 if (image->colormap[i].red == image->colormap[0].red &&
9725 image->colormap[i].green == image->colormap[0].green &&
9726 image->colormap[i].blue == image->colormap[0].blue)
9728 ping_have_cheap_transparency = MagickFalse;
9733 if (logging != MagickFalse)
9735 if (ping_have_cheap_transparency == MagickFalse)
9736 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9737 " Cheap transparency is not possible.");
9740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9741 " Cheap transparency is possible.");
9745 ping_have_cheap_transparency = MagickFalse;
9747 image_depth=image->depth;
9749 quantum_info = (QuantumInfo *) NULL;
9751 image_colors=(int) image->colors;
9752 image_matte=image->alpha_trait !=
9753 UndefinedPixelTrait ? MagickTrue : MagickFalse;
9755 if (mng_info->write_png_colortype < 5)
9756 mng_info->IsPalette=image->storage_class == PseudoClass &&
9757 image_colors <= 256 && image->colormap != NULL;
9759 mng_info->IsPalette = MagickFalse;
9761 if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9762 (image->colors == 0 || image->colormap == NULL))
9764 image_info=DestroyImageInfo(image_info);
9765 image=DestroyImage(image);
9766 (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9767 "Cannot write PNG8 or color-type 3; colormap is NULL",
9768 "`%s'",IMimage->filename);
9769 return(MagickFalse);
9773 Allocate the PNG structures
9775 #ifdef PNG_USER_MEM_SUPPORTED
9776 error_info.image=image;
9777 error_info.exception=exception;
9778 ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9779 MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9780 (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9783 ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9784 MagickPNGErrorHandler,MagickPNGWarningHandler);
9787 if (ping == (png_struct *) NULL)
9788 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9790 ping_info=png_create_info_struct(ping);
9792 if (ping_info == (png_info *) NULL)
9794 png_destroy_write_struct(&ping,(png_info **) NULL);
9795 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9798 png_set_write_fn(ping,image,png_put_data,png_flush_data);
9799 pixel_info=(MemoryInfo *) NULL;
9801 if (setjmp(png_jmpbuf(ping)))
9807 if (image_info->verbose)
9808 (void) printf("PNG write has failed.\n");
9810 png_destroy_write_struct(&ping,&ping_info);
9811 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9812 UnlockSemaphoreInfo(ping_semaphore);
9815 if (pixel_info != (MemoryInfo *) NULL)
9816 pixel_info=RelinquishVirtualMemory(pixel_info);
9818 if (quantum_info != (QuantumInfo *) NULL)
9819 quantum_info=DestroyQuantumInfo(quantum_info);
9821 if (ping_have_blob != MagickFalse)
9822 (void) CloseBlob(image);
9823 image_info=DestroyImageInfo(image_info);
9824 image=DestroyImage(image);
9825 return(MagickFalse);
9828 /* { For navigation to end of SETJMP-protected block. Within this
9829 * block, use png_error() instead of Throwing an Exception, to ensure
9830 * that libpng is able to clean up, and that the semaphore is unlocked.
9833 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9834 LockSemaphoreInfo(ping_semaphore);
9837 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9838 /* Allow benign errors */
9839 png_set_benign_errors(ping, 1);
9842 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9843 /* Reject images with too many rows or columns */
9844 png_set_user_limits(ping,
9845 (png_uint_32) MagickMin(0x7fffffffL,
9846 GetMagickResourceLimit(WidthResource)),
9847 (png_uint_32) MagickMin(0x7fffffffL,
9848 GetMagickResourceLimit(HeightResource)));
9849 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9852 Prepare PNG for writing.
9855 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9856 if (mng_info->write_mng)
9858 (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9859 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9860 /* Disable new libpng-1.5.10 feature when writing a MNG because
9861 * zero-length PLTE is OK
9863 png_set_check_for_invalid_index (ping, 0);
9868 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9869 if (mng_info->write_mng)
9870 png_permit_empty_plte(ping,MagickTrue);
9877 ping_width=(png_uint_32) image->columns;
9878 ping_height=(png_uint_32) image->rows;
9880 if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9883 if (mng_info->write_png48 || mng_info->write_png64)
9886 if (mng_info->write_png_depth != 0)
9887 image_depth=mng_info->write_png_depth;
9889 /* Adjust requested depth to next higher valid depth if necessary */
9890 if (image_depth > 8)
9893 if ((image_depth > 4) && (image_depth < 8))
9896 if (image_depth == 3)
9899 if (logging != MagickFalse)
9901 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9902 " width=%.20g",(double) ping_width);
9903 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9904 " height=%.20g",(double) ping_height);
9905 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9906 " image_matte=%.20g",(double) image->alpha_trait);
9907 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9908 " image->depth=%.20g",(double) image->depth);
9909 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9910 " Tentative ping_bit_depth=%.20g",(double) image_depth);
9913 save_image_depth=image_depth;
9914 ping_bit_depth=(png_byte) save_image_depth;
9917 #if defined(PNG_pHYs_SUPPORTED)
9918 if (ping_exclude_pHYs == MagickFalse)
9920 if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9921 (!mng_info->write_mng || !mng_info->equal_physs))
9923 if (logging != MagickFalse)
9924 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9925 " Setting up pHYs chunk");
9927 if (image->units == PixelsPerInchResolution)
9929 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9930 ping_pHYs_x_resolution=
9931 (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9932 ping_pHYs_y_resolution=
9933 (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9936 else if (image->units == PixelsPerCentimeterResolution)
9938 ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9939 ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9940 ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9945 ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9946 ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9947 ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9950 if (logging != MagickFalse)
9951 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9952 " Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9953 (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9954 (int) ping_pHYs_unit_type);
9955 ping_have_pHYs = MagickTrue;
9960 if (ping_exclude_bKGD == MagickFalse)
9962 if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9968 if (ping_bit_depth == 8)
9971 if (ping_bit_depth == 4)
9974 if (ping_bit_depth == 2)
9977 if (ping_bit_depth == 1)
9980 ping_background.red=(png_uint_16)
9981 (ScaleQuantumToShort(image->background_color.red) & mask);
9983 ping_background.green=(png_uint_16)
9984 (ScaleQuantumToShort(image->background_color.green) & mask);
9986 ping_background.blue=(png_uint_16)
9987 (ScaleQuantumToShort(image->background_color.blue) & mask);
9989 ping_background.gray=(png_uint_16) ping_background.green;
9992 if (logging != MagickFalse)
9994 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9995 " Setting up bKGD chunk (1)");
9996 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9997 " background_color index is %d",
9998 (int) ping_background.index);
10000 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10001 " ping_bit_depth=%d",ping_bit_depth);
10004 ping_have_bKGD = MagickTrue;
10008 Select the color type.
10013 if (mng_info->IsPalette && mng_info->write_png8)
10015 /* To do: make this a function cause it's used twice, except
10016 for reducing the sample depth from 8. */
10018 number_colors=image_colors;
10020 ping_have_tRNS=MagickFalse;
10025 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10027 if (logging != MagickFalse)
10028 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029 " Setting up PLTE chunk with %d colors (%d)",
10030 number_colors, image_colors);
10032 for (i=0; i < (ssize_t) number_colors; i++)
10034 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10035 palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10036 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10037 if (logging != MagickFalse)
10038 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039 #if MAGICKCORE_QUANTUM_DEPTH == 8
10040 " %3ld (%3d,%3d,%3d)",
10042 " %5ld (%5d,%5d,%5d)",
10044 (long) i,palette[i].red,palette[i].green,palette[i].blue);
10048 ping_have_PLTE=MagickTrue;
10049 image_depth=ping_bit_depth;
10052 if (matte != MagickFalse)
10055 Identify which colormap entry is transparent.
10057 assert(number_colors <= 256);
10058 assert(image->colormap != NULL);
10060 for (i=0; i < (ssize_t) number_transparent; i++)
10061 ping_trans_alpha[i]=0;
10064 ping_num_trans=(unsigned short) (number_transparent +
10065 number_semitransparent);
10067 if (ping_num_trans == 0)
10068 ping_have_tRNS=MagickFalse;
10071 ping_have_tRNS=MagickTrue;
10074 if (ping_exclude_bKGD == MagickFalse)
10077 * Identify which colormap entry is the background color.
10080 for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10081 if (IsPNGColorEqual(ping_background,image->colormap[i]))
10084 ping_background.index=(png_byte) i;
10086 if (logging != MagickFalse)
10088 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10089 " background_color index is %d",
10090 (int) ping_background.index);
10093 } /* end of write_png8 */
10095 else if (mng_info->write_png_colortype == 1)
10097 image_matte=MagickFalse;
10098 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10101 else if (mng_info->write_png24 || mng_info->write_png48 ||
10102 mng_info->write_png_colortype == 3)
10104 image_matte=MagickFalse;
10105 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10108 else if (mng_info->write_png32 || mng_info->write_png64 ||
10109 mng_info->write_png_colortype == 7)
10111 image_matte=MagickTrue;
10112 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10115 else /* mng_info->write_pngNN not specified */
10117 image_depth=ping_bit_depth;
10119 if (mng_info->write_png_colortype != 0)
10121 ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10123 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10124 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10125 image_matte=MagickTrue;
10128 image_matte=MagickFalse;
10130 if (logging != MagickFalse)
10131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10132 " PNG colortype %d was specified:",(int) ping_color_type);
10135 else /* write_png_colortype not specified */
10137 if (logging != MagickFalse)
10138 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10139 " Selecting PNG colortype:");
10141 ping_color_type=(png_byte) ((matte != MagickFalse)?
10142 PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
10144 if (image_info->type == TrueColorType)
10146 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10147 image_matte=MagickFalse;
10150 if (image_info->type == TrueColorAlphaType)
10152 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10153 image_matte=MagickTrue;
10156 if (image_info->type == PaletteType ||
10157 image_info->type == PaletteAlphaType)
10158 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10160 if (mng_info->write_png_colortype == 0 &&
10161 image_info->type == UndefinedType)
10163 if (ping_have_color == MagickFalse)
10165 if (image_matte == MagickFalse)
10167 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10168 image_matte=MagickFalse;
10173 ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10174 image_matte=MagickTrue;
10179 if (image_matte == MagickFalse)
10181 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10182 image_matte=MagickFalse;
10187 ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10188 image_matte=MagickTrue;
10195 if (logging != MagickFalse)
10196 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10197 " Selected PNG colortype=%d",ping_color_type);
10199 if (ping_bit_depth < 8)
10201 if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10202 ping_color_type == PNG_COLOR_TYPE_RGB ||
10203 ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10207 old_bit_depth=ping_bit_depth;
10209 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10211 if (image->alpha_trait == UndefinedPixelTrait &&
10212 ping_have_non_bw == MagickFalse)
10216 if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10221 if (image->colors == 0)
10224 png_error(ping,"image has 0 colors");
10227 while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10228 ping_bit_depth <<= 1;
10231 if (logging != MagickFalse)
10233 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10234 " Number of colors: %.20g",(double) image_colors);
10236 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10237 " Tentative PNG bit depth: %d",ping_bit_depth);
10240 if (ping_bit_depth < (int) mng_info->write_png_depth)
10241 ping_bit_depth = mng_info->write_png_depth;
10244 image_depth=ping_bit_depth;
10246 if (logging != MagickFalse)
10248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10249 " Tentative PNG color type: %s (%.20g)",
10250 PngColorTypeToString(ping_color_type),
10251 (double) ping_color_type);
10253 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10254 " image_info->type: %.20g",(double) image_info->type);
10256 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10257 " image_depth: %.20g",(double) image_depth);
10259 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10261 " image->depth: %.20g",(double) image->depth);
10263 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10264 " ping_bit_depth: %.20g",(double) ping_bit_depth);
10267 if (matte != MagickFalse)
10269 if (mng_info->IsPalette)
10271 if (mng_info->write_png_colortype == 0)
10273 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10275 if (ping_have_color != MagickFalse)
10276 ping_color_type=PNG_COLOR_TYPE_RGBA;
10280 * Determine if there is any transparent color.
10282 if (number_transparent + number_semitransparent == 0)
10285 No transparent pixels are present. Change 4 or 6 to 0 or 2.
10288 image_matte=MagickFalse;
10290 if (mng_info->write_png_colortype == 0)
10291 ping_color_type&=0x03;
10301 if (ping_bit_depth == 8)
10304 if (ping_bit_depth == 4)
10307 if (ping_bit_depth == 2)
10310 if (ping_bit_depth == 1)
10313 ping_trans_color.red=(png_uint_16)
10314 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10316 ping_trans_color.green=(png_uint_16)
10317 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10319 ping_trans_color.blue=(png_uint_16)
10320 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10322 ping_trans_color.gray=(png_uint_16)
10323 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10324 image->colormap)) & mask);
10326 ping_trans_color.index=(png_byte) 0;
10328 ping_have_tRNS=MagickTrue;
10331 if (ping_have_tRNS != MagickFalse)
10334 * Determine if there is one and only one transparent color
10335 * and if so if it is fully transparent.
10337 if (ping_have_cheap_transparency == MagickFalse)
10338 ping_have_tRNS=MagickFalse;
10341 if (ping_have_tRNS != MagickFalse)
10343 if (mng_info->write_png_colortype == 0)
10344 ping_color_type &= 0x03; /* changes 4 or 6 to 0 or 2 */
10346 if (image_depth == 8)
10348 ping_trans_color.red&=0xff;
10349 ping_trans_color.green&=0xff;
10350 ping_trans_color.blue&=0xff;
10351 ping_trans_color.gray&=0xff;
10357 if (image_depth == 8)
10359 ping_trans_color.red&=0xff;
10360 ping_trans_color.green&=0xff;
10361 ping_trans_color.blue&=0xff;
10362 ping_trans_color.gray&=0xff;
10369 if (ping_have_tRNS != MagickFalse)
10370 image_matte=MagickFalse;
10372 if ((mng_info->IsPalette) &&
10373 mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10374 ping_have_color == MagickFalse &&
10375 (image_matte == MagickFalse || image_depth >= 8))
10379 if (image_matte != MagickFalse)
10380 ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10382 else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10384 ping_color_type=PNG_COLOR_TYPE_GRAY;
10386 if (save_image_depth == 16 && image_depth == 8)
10388 if (logging != MagickFalse)
10390 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10391 " Scaling ping_trans_color (0)");
10393 ping_trans_color.gray*=0x0101;
10397 if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10398 image_depth=MAGICKCORE_QUANTUM_DEPTH;
10400 if ((image_colors == 0) ||
10401 ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10402 image_colors=(int) (one << image_depth);
10404 if (image_depth > 8)
10410 if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10412 if(!mng_info->write_png_depth)
10416 while ((int) (one << ping_bit_depth)
10417 < (ssize_t) image_colors)
10418 ping_bit_depth <<= 1;
10422 else if (ping_color_type ==
10423 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10424 mng_info->IsPalette)
10426 /* Check if grayscale is reducible */
10429 depth_4_ok=MagickTrue,
10430 depth_2_ok=MagickTrue,
10431 depth_1_ok=MagickTrue;
10433 for (i=0; i < (ssize_t) image_colors; i++)
10438 intensity=ScaleQuantumToChar(image->colormap[i].red);
10440 if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10441 depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10442 else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10443 depth_2_ok=depth_1_ok=MagickFalse;
10444 else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10445 depth_1_ok=MagickFalse;
10448 if (depth_1_ok && mng_info->write_png_depth <= 1)
10451 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10454 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10459 image_depth=ping_bit_depth;
10464 if (mng_info->IsPalette)
10466 number_colors=image_colors;
10468 if (image_depth <= 8)
10473 ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10475 if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10477 for (i=0; i < (ssize_t) number_colors; i++)
10479 palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10481 ScaleQuantumToChar(image->colormap[i].green);
10482 palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10485 if (logging != MagickFalse)
10486 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10487 " Setting up PLTE chunk with %d colors",
10490 ping_have_PLTE=MagickTrue;
10493 /* color_type is PNG_COLOR_TYPE_PALETTE */
10494 if (mng_info->write_png_depth == 0)
10502 while ((one << ping_bit_depth) < (size_t) number_colors)
10503 ping_bit_depth <<= 1;
10508 if (matte != MagickFalse)
10511 * Set up trans_colors array.
10513 assert(number_colors <= 256);
10515 ping_num_trans=(unsigned short) (number_transparent +
10516 number_semitransparent);
10518 if (ping_num_trans == 0)
10519 ping_have_tRNS=MagickFalse;
10523 if (logging != MagickFalse)
10525 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10526 " Scaling ping_trans_color (1)");
10528 ping_have_tRNS=MagickTrue;
10530 for (i=0; i < ping_num_trans; i++)
10532 ping_trans_alpha[i]= (png_byte)
10533 ScaleQuantumToChar(image->colormap[i].alpha);
10543 if (image_depth < 8)
10546 if ((save_image_depth == 16) && (image_depth == 8))
10548 if (logging != MagickFalse)
10550 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551 " Scaling ping_trans_color from (%d,%d,%d)",
10552 (int) ping_trans_color.red,
10553 (int) ping_trans_color.green,
10554 (int) ping_trans_color.blue);
10557 ping_trans_color.red*=0x0101;
10558 ping_trans_color.green*=0x0101;
10559 ping_trans_color.blue*=0x0101;
10560 ping_trans_color.gray*=0x0101;
10562 if (logging != MagickFalse)
10564 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10566 (int) ping_trans_color.red,
10567 (int) ping_trans_color.green,
10568 (int) ping_trans_color.blue);
10573 if (ping_bit_depth < (ssize_t) mng_info->write_png_depth)
10574 ping_bit_depth = (ssize_t) mng_info->write_png_depth;
10577 Adjust background and transparency samples in sub-8-bit grayscale files.
10579 if (ping_bit_depth < 8 && ping_color_type ==
10580 PNG_COLOR_TYPE_GRAY)
10588 maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10590 if (ping_exclude_bKGD == MagickFalse)
10593 ping_background.gray=(png_uint_16) ((maxval/65535.)*
10594 (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10595 &image->background_color))) +.5)));
10597 if (logging != MagickFalse)
10598 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10599 " Setting up bKGD chunk (2)");
10600 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10601 " background_color index is %d",
10602 (int) ping_background.index);
10604 ping_have_bKGD = MagickTrue;
10607 if (logging != MagickFalse)
10608 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10609 " Scaling ping_trans_color.gray from %d",
10610 (int)ping_trans_color.gray);
10612 ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10613 ping_trans_color.gray)+.5);
10615 if (logging != MagickFalse)
10616 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10617 " to %d", (int)ping_trans_color.gray);
10620 if (ping_exclude_bKGD == MagickFalse)
10622 if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10625 Identify which colormap entry is the background color.
10628 number_colors=image_colors;
10630 for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10631 if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10634 ping_background.index=(png_byte) i;
10636 if (logging != MagickFalse)
10638 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10639 " Setting up bKGD chunk with index=%d",(int) i);
10642 if (i < (ssize_t) number_colors)
10644 ping_have_bKGD = MagickTrue;
10646 if (logging != MagickFalse)
10648 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10649 " background =(%d,%d,%d)",
10650 (int) ping_background.red,
10651 (int) ping_background.green,
10652 (int) ping_background.blue);
10656 else /* Can't happen */
10658 if (logging != MagickFalse)
10659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10660 " No room in PLTE to add bKGD color");
10661 ping_have_bKGD = MagickFalse;
10666 if (logging != MagickFalse)
10667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10668 " PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10671 Initialize compression level and filtering.
10673 if (logging != MagickFalse)
10675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10676 " Setting up deflate compression");
10678 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679 " Compression buffer size: 32768");
10682 png_set_compression_buffer_size(ping,32768L);
10684 if (logging != MagickFalse)
10685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10686 " Compression mem level: 9");
10688 png_set_compression_mem_level(ping, 9);
10690 /* Untangle the "-quality" setting:
10692 Undefined is 0; the default is used.
10697 0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10698 zlib default compression level
10700 1-9: the zlib compression level
10704 0-4: the PNG filter method
10706 5: libpng adaptive filtering if compression level > 5
10707 libpng filter type "none" if compression level <= 5
10708 or if image is grayscale or palette
10710 6: libpng adaptive filtering
10712 7: "LOCO" filtering (intrapixel differing) if writing
10713 a MNG, otherwise "none". Did not work in IM-6.7.0-9
10714 and earlier because of a missing "else".
10716 8: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10717 filtering. Unused prior to IM-6.7.0-10, was same as 6
10719 9: Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10720 Unused prior to IM-6.7.0-10, was same as 6
10722 Note that using the -quality option, not all combinations of
10723 PNG filter type, zlib compression level, and zlib compression
10724 strategy are possible. This will be addressed soon in a
10725 release that accomodates "-define png:compression-strategy", etc.
10729 quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10730 image_info->quality;
10734 if (mng_info->write_png_compression_strategy == 0)
10735 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10738 else if (mng_info->write_png_compression_level == 0)
10743 level=(int) MagickMin((ssize_t) quality/10,9);
10745 mng_info->write_png_compression_level = level+1;
10748 if (mng_info->write_png_compression_strategy == 0)
10750 if ((quality %10) == 8 || (quality %10) == 9)
10751 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
10752 mng_info->write_png_compression_strategy=Z_RLE+1;
10754 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10758 if (mng_info->write_png_compression_filter == 0)
10759 mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10761 if (logging != MagickFalse)
10763 if (mng_info->write_png_compression_level)
10764 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10765 " Compression level: %d",
10766 (int) mng_info->write_png_compression_level-1);
10768 if (mng_info->write_png_compression_strategy)
10769 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10770 " Compression strategy: %d",
10771 (int) mng_info->write_png_compression_strategy-1);
10773 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10774 " Setting up filtering");
10776 if (mng_info->write_png_compression_filter == 6)
10777 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10778 " Base filter method: ADAPTIVE");
10779 else if (mng_info->write_png_compression_filter == 0 ||
10780 mng_info->write_png_compression_filter == 1)
10781 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10782 " Base filter method: NONE");
10784 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10785 " Base filter method: %d",
10786 (int) mng_info->write_png_compression_filter-1);
10789 if (mng_info->write_png_compression_level != 0)
10790 png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10792 if (mng_info->write_png_compression_filter == 6)
10794 if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10795 ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10797 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10799 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10801 else if (mng_info->write_png_compression_filter == 7 ||
10802 mng_info->write_png_compression_filter == 10)
10803 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10805 else if (mng_info->write_png_compression_filter == 8)
10807 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10808 if (mng_info->write_mng)
10810 if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10811 ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10812 ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10815 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10818 else if (mng_info->write_png_compression_filter == 9)
10819 png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10821 else if (mng_info->write_png_compression_filter != 0)
10822 png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10823 mng_info->write_png_compression_filter-1);
10825 if (mng_info->write_png_compression_strategy != 0)
10826 png_set_compression_strategy(ping,
10827 mng_info->write_png_compression_strategy-1);
10829 ping_interlace_method=image_info->interlace != NoInterlace;
10831 if (mng_info->write_mng)
10832 png_set_sig_bytes(ping,8);
10834 /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10836 if (mng_info->write_png_colortype != 0)
10838 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10839 if (ping_have_color != MagickFalse)
10841 ping_color_type = PNG_COLOR_TYPE_RGB;
10843 if (ping_bit_depth < 8)
10847 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10848 if (ping_have_color != MagickFalse)
10849 ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10852 if (ping_need_colortype_warning != MagickFalse ||
10853 ((mng_info->write_png_depth &&
10854 (int) mng_info->write_png_depth != ping_bit_depth) ||
10855 (mng_info->write_png_colortype &&
10856 ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10857 mng_info->write_png_colortype != 7 &&
10858 !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10860 if (logging != MagickFalse)
10862 if (ping_need_colortype_warning != MagickFalse)
10864 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10865 " Image has transparency but tRNS chunk was excluded");
10868 if (mng_info->write_png_depth)
10870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10871 " Defined png:bit-depth=%u, Computed depth=%u",
10872 mng_info->write_png_depth,
10876 if (mng_info->write_png_colortype)
10878 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10879 " Defined png:color-type=%u, Computed color type=%u",
10880 mng_info->write_png_colortype-1,
10886 "Cannot write image with defined png:bit-depth or png:color-type.");
10889 if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10891 /* Add an opaque matte channel */
10892 image->alpha_trait = BlendPixelTrait;
10893 (void) SetImageAlpha(image,OpaqueAlpha,exception);
10895 if (logging != MagickFalse)
10896 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10897 " Added an opaque matte channel");
10900 if (number_transparent != 0 || number_semitransparent != 0)
10902 if (ping_color_type < 4)
10904 ping_have_tRNS=MagickTrue;
10905 if (logging != MagickFalse)
10906 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10907 " Setting ping_have_tRNS=MagickTrue.");
10911 if (logging != MagickFalse)
10912 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10913 " Writing PNG header chunks");
10915 png_set_IHDR(ping,ping_info,ping_width,ping_height,
10916 ping_bit_depth,ping_color_type,
10917 ping_interlace_method,ping_compression_method,
10918 ping_filter_method);
10920 if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10922 png_set_PLTE(ping,ping_info,palette,number_colors);
10924 if (logging != MagickFalse)
10926 for (i=0; i< (ssize_t) number_colors; i++)
10928 if (i < ping_num_trans)
10929 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10930 " PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10932 (int) palette[i].red,
10933 (int) palette[i].green,
10934 (int) palette[i].blue,
10936 (int) ping_trans_alpha[i]);
10938 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10939 " PLTE[%d] = (%d,%d,%d)",
10941 (int) palette[i].red,
10942 (int) palette[i].green,
10943 (int) palette[i].blue);
10948 /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10949 if (ping_exclude_sRGB != MagickFalse ||
10950 (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10952 if ((ping_exclude_tEXt == MagickFalse ||
10953 ping_exclude_zTXt == MagickFalse) &&
10954 (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10956 ResetImageProfileIterator(image);
10957 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10959 profile=GetImageProfile(image,name);
10961 if (profile != (StringInfo *) NULL)
10963 #ifdef PNG_WRITE_iCCP_SUPPORTED
10964 if ((LocaleCompare(name,"ICC") == 0) ||
10965 (LocaleCompare(name,"ICM") == 0))
10967 ping_have_iCCP = MagickTrue;
10968 if (ping_exclude_iCCP == MagickFalse)
10970 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10971 " Setting up iCCP chunk");
10973 png_set_iCCP(ping,ping_info,(png_charp) name,0,
10974 #if (PNG_LIBPNG_VER < 10500)
10975 (png_charp) GetStringInfoDatum(profile),
10977 (const png_byte *) GetStringInfoDatum(profile),
10979 (png_uint_32) GetStringInfoLength(profile));
10983 /* Do not write hex-encoded ICC chunk */
10984 name=GetNextImageProfile(image);
10988 #endif /* WRITE_iCCP */
10990 #if defined(eXIf_SUPPORTED) || defined(exIf_SUPPORTED) || \
10991 defined(zxIf_SUPPORTED)
10992 if (LocaleCompare(name,"exif") == 0)
10994 /* Do not write hex-encoded ICC chunk; we will
10995 write it later as an eXIf or zxIf chunk */
10996 name=GetNextImageProfile(image);
11001 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11002 " Setting up zTXt chunk with uuencoded %s profile",
11004 Magick_png_write_raw_profile(image_info,ping,ping_info,
11005 (unsigned char *) name,(unsigned char *) name,
11006 GetStringInfoDatum(profile),
11007 (png_uint_32) GetStringInfoLength(profile));
11009 name=GetNextImageProfile(image);
11014 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11015 if ((mng_info->have_write_global_srgb == 0) &&
11016 ping_have_iCCP != MagickTrue &&
11017 (ping_have_sRGB != MagickFalse ||
11018 png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11020 if (ping_exclude_sRGB == MagickFalse)
11023 Note image rendering intent.
11025 if (logging != MagickFalse)
11026 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11027 " Setting up sRGB chunk");
11029 (void) png_set_sRGB(ping,ping_info,(
11030 Magick_RenderingIntent_to_PNG_RenderingIntent(
11031 image->rendering_intent)));
11033 ping_have_sRGB = MagickTrue;
11037 if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11040 if (ping_exclude_gAMA == MagickFalse &&
11041 ping_have_iCCP == MagickFalse &&
11042 ping_have_sRGB == MagickFalse &&
11043 (ping_exclude_sRGB == MagickFalse ||
11044 (image->gamma < .45 || image->gamma > .46)))
11046 if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11050 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11052 if (logging != MagickFalse)
11053 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11054 " Setting up gAMA chunk");
11056 png_set_gAMA(ping,ping_info,image->gamma);
11060 if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11062 if ((mng_info->have_write_global_chrm == 0) &&
11063 (image->chromaticity.red_primary.x != 0.0))
11066 Note image chromaticity.
11067 Note: if cHRM+gAMA == sRGB write sRGB instead.
11075 wp=image->chromaticity.white_point;
11076 rp=image->chromaticity.red_primary;
11077 gp=image->chromaticity.green_primary;
11078 bp=image->chromaticity.blue_primary;
11080 if (logging != MagickFalse)
11081 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11082 " Setting up cHRM chunk");
11084 png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11090 if (ping_exclude_bKGD == MagickFalse)
11092 if (ping_have_bKGD != MagickFalse)
11094 png_set_bKGD(ping,ping_info,&ping_background);
11095 if (logging != MagickFalse)
11097 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11098 " Setting up bKGD chunk");
11099 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11100 " background color = (%d,%d,%d)",
11101 (int) ping_background.red,
11102 (int) ping_background.green,
11103 (int) ping_background.blue);
11104 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11105 " index = %d, gray=%d",
11106 (int) ping_background.index,
11107 (int) ping_background.gray);
11112 if (ping_exclude_pHYs == MagickFalse)
11114 if (ping_have_pHYs != MagickFalse)
11116 png_set_pHYs(ping,ping_info,
11117 ping_pHYs_x_resolution,
11118 ping_pHYs_y_resolution,
11119 ping_pHYs_unit_type);
11121 if (logging != MagickFalse)
11123 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11124 " Setting up pHYs chunk");
11125 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11126 " x_resolution=%lu",
11127 (unsigned long) ping_pHYs_x_resolution);
11128 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11129 " y_resolution=%lu",
11130 (unsigned long) ping_pHYs_y_resolution);
11131 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11133 (unsigned long) ping_pHYs_unit_type);
11138 #if defined(PNG_tIME_SUPPORTED)
11139 if (ping_exclude_tIME == MagickFalse)
11144 if (image->taint == MagickFalse)
11146 timestamp=GetImageOption(image_info,"png:tIME");
11148 if (timestamp == (const char *) NULL)
11149 timestamp=GetImageProperty(image,"png:tIME",exception);
11154 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11155 " Reset tIME in tainted image");
11157 timestamp=GetImageProperty(image,"date:modify",exception);
11160 if (timestamp != (const char *) NULL)
11161 write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11165 if (mng_info->need_blob != MagickFalse)
11167 if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11169 png_error(ping,"WriteBlob Failed");
11171 ping_have_blob=MagickTrue;
11174 png_write_info_before_PLTE(ping, ping_info);
11176 if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11178 if (logging != MagickFalse)
11180 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11181 " Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11184 if (ping_color_type == 3)
11185 (void) png_set_tRNS(ping, ping_info,
11192 (void) png_set_tRNS(ping, ping_info,
11195 &ping_trans_color);
11197 if (logging != MagickFalse)
11199 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11200 " tRNS color =(%d,%d,%d)",
11201 (int) ping_trans_color.red,
11202 (int) ping_trans_color.green,
11203 (int) ping_trans_color.blue);
11208 /* write any png-chunk-b profiles */
11209 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
11211 png_write_info(ping,ping_info);
11213 /* write any PNG-chunk-m profiles */
11214 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
11216 ping_wrote_caNv = MagickFalse;
11218 /* write caNv chunk */
11219 if (ping_exclude_caNv == MagickFalse)
11221 if ((image->page.width != 0 && image->page.width != image->columns) ||
11222 (image->page.height != 0 && image->page.height != image->rows) ||
11223 image->page.x != 0 || image->page.y != 0)
11228 (void) WriteBlobMSBULong(image,16L); /* data length=8 */
11229 PNGType(chunk,mng_caNv);
11230 LogPNGChunk(logging,mng_caNv,16L);
11231 PNGLong(chunk+4,(png_uint_32) image->page.width);
11232 PNGLong(chunk+8,(png_uint_32) image->page.height);
11233 PNGsLong(chunk+12,(png_int_32) image->page.x);
11234 PNGsLong(chunk+16,(png_int_32) image->page.y);
11235 (void) WriteBlob(image,20,chunk);
11236 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11237 ping_wrote_caNv = MagickTrue;
11241 #if defined(PNG_oFFs_SUPPORTED)
11242 if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11244 if (image->page.x || image->page.y)
11246 png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11247 (png_int_32) image->page.y, 0);
11249 if (logging != MagickFalse)
11250 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11251 " Setting up oFFs chunk with x=%d, y=%d, units=0",
11252 (int) image->page.x, (int) image->page.y);
11257 /* write vpAg chunk (deprecated, replaced by caNv) */
11258 if (ping_exclude_vpAg == MagickFalse && ping_wrote_caNv == MagickFalse)
11260 if ((image->page.width != 0 && image->page.width != image->columns) ||
11261 (image->page.height != 0 && image->page.height != image->rows))
11266 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
11267 PNGType(chunk,mng_vpAg);
11268 LogPNGChunk(logging,mng_vpAg,9L);
11269 PNGLong(chunk+4,(png_uint_32) image->page.width);
11270 PNGLong(chunk+8,(png_uint_32) image->page.height);
11271 chunk[12]=0; /* unit = pixels */
11272 (void) WriteBlob(image,13,chunk);
11273 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11277 #if (PNG_LIBPNG_VER == 10206)
11278 /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11279 #define PNG_HAVE_IDAT 0x04
11280 ping->mode |= PNG_HAVE_IDAT;
11281 #undef PNG_HAVE_IDAT
11284 png_set_packing(ping);
11288 rowbytes=image->columns;
11289 if (image_depth > 8)
11291 switch (ping_color_type)
11293 case PNG_COLOR_TYPE_RGB:
11297 case PNG_COLOR_TYPE_GRAY_ALPHA:
11301 case PNG_COLOR_TYPE_RGBA:
11309 if (logging != MagickFalse)
11311 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11312 " Writing PNG image data");
11314 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11315 " Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11317 pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11318 if (pixel_info == (MemoryInfo *) NULL)
11319 png_error(ping,"Allocation of memory for pixels failed");
11320 ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11323 Initialize image scanlines.
11325 quantum_info=AcquireQuantumInfo(image_info,image);
11326 if (quantum_info == (QuantumInfo *) NULL)
11327 png_error(ping,"Memory allocation for quantum_info failed");
11328 quantum_info->format=UndefinedQuantumFormat;
11329 SetQuantumDepth(image,quantum_info,image_depth);
11330 (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11331 num_passes=png_set_interlace_handling(ping);
11333 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11334 !mng_info->write_png48 && !mng_info->write_png64 &&
11335 !mng_info->write_png32) &&
11336 (mng_info->IsPalette ||
11337 (image_info->type == BilevelType)) &&
11338 image_matte == MagickFalse &&
11339 ping_have_non_bw == MagickFalse)
11341 /* Palette, Bilevel, or Opaque Monochrome */
11342 register const Quantum
11345 SetQuantumDepth(image,quantum_info,8);
11346 for (pass=0; pass < num_passes; pass++)
11349 Convert PseudoClass image to a PNG monochrome image.
11351 for (y=0; y < (ssize_t) image->rows; y++)
11353 if (logging != MagickFalse && y == 0)
11354 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11355 " Writing row of pixels (0)");
11357 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11359 if (p == (const Quantum *) NULL)
11362 if (mng_info->IsPalette)
11364 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11365 quantum_info,GrayQuantum,ping_pixels,exception);
11366 if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11367 mng_info->write_png_depth &&
11368 mng_info->write_png_depth != old_bit_depth)
11370 /* Undo pixel scaling */
11371 for (i=0; i < (ssize_t) image->columns; i++)
11372 *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11373 >> (8-old_bit_depth));
11379 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11380 quantum_info,RedQuantum,ping_pixels,exception);
11383 if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11384 for (i=0; i < (ssize_t) image->columns; i++)
11385 *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11388 if (logging != MagickFalse && y == 0)
11389 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11390 " Writing row of pixels (1)");
11392 png_write_row(ping,ping_pixels);
11394 status=SetImageProgress(image,SaveImageTag,
11395 (MagickOffsetType) (pass * image->rows + y),
11396 num_passes * image->rows);
11398 if (status == MagickFalse)
11404 else /* Not Palette, Bilevel, or Opaque Monochrome */
11406 if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11407 !mng_info->write_png48 && !mng_info->write_png64 &&
11408 !mng_info->write_png32) && (image_matte != MagickFalse ||
11409 (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11410 (mng_info->IsPalette) && ping_have_color == MagickFalse)
11412 register const Quantum
11415 for (pass=0; pass < num_passes; pass++)
11418 for (y=0; y < (ssize_t) image->rows; y++)
11420 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11422 if (p == (const Quantum *) NULL)
11425 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11427 if (mng_info->IsPalette)
11428 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11429 quantum_info,GrayQuantum,ping_pixels,exception);
11432 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11433 quantum_info,RedQuantum,ping_pixels,exception);
11435 if (logging != MagickFalse && y == 0)
11436 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11437 " Writing GRAY PNG pixels (2)");
11440 else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11442 if (logging != MagickFalse && y == 0)
11443 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11444 " Writing GRAY_ALPHA PNG pixels (2)");
11446 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11447 quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11450 if (logging != MagickFalse && y == 0)
11451 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11452 " Writing row of pixels (2)");
11454 png_write_row(ping,ping_pixels);
11456 status=SetImageProgress(image,SaveImageTag,
11457 (MagickOffsetType) (pass * image->rows + y),
11458 num_passes * image->rows);
11460 if (status == MagickFalse)
11468 register const Quantum
11471 for (pass=0; pass < num_passes; pass++)
11473 if ((image_depth > 8) ||
11474 mng_info->write_png24 ||
11475 mng_info->write_png32 ||
11476 mng_info->write_png48 ||
11477 mng_info->write_png64 ||
11478 (!mng_info->write_png8 && !mng_info->IsPalette))
11480 for (y=0; y < (ssize_t) image->rows; y++)
11482 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11484 if (p == (const Quantum *) NULL)
11487 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11489 if (image->storage_class == DirectClass)
11490 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11491 quantum_info,RedQuantum,ping_pixels,exception);
11494 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11495 quantum_info,GrayQuantum,ping_pixels,exception);
11498 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11500 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11501 quantum_info,GrayAlphaQuantum,ping_pixels,
11504 if (logging != MagickFalse && y == 0)
11505 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11506 " Writing GRAY_ALPHA PNG pixels (3)");
11509 else if (image_matte != MagickFalse)
11510 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11511 quantum_info,RGBAQuantum,ping_pixels,exception);
11514 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11515 quantum_info,RGBQuantum,ping_pixels,exception);
11517 if (logging != MagickFalse && y == 0)
11518 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11519 " Writing row of pixels (3)");
11521 png_write_row(ping,ping_pixels);
11523 status=SetImageProgress(image,SaveImageTag,
11524 (MagickOffsetType) (pass * image->rows + y),
11525 num_passes * image->rows);
11527 if (status == MagickFalse)
11533 /* not ((image_depth > 8) ||
11534 mng_info->write_png24 || mng_info->write_png32 ||
11535 mng_info->write_png48 || mng_info->write_png64 ||
11536 (!mng_info->write_png8 && !mng_info->IsPalette))
11539 if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11540 (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11542 if (logging != MagickFalse)
11543 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11544 " pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11546 SetQuantumDepth(image,quantum_info,8);
11550 for (y=0; y < (ssize_t) image->rows; y++)
11552 if (logging != MagickFalse && y == 0)
11553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11554 " pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11557 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11559 if (p == (const Quantum *) NULL)
11562 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11564 SetQuantumDepth(image,quantum_info,image->depth);
11566 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11567 quantum_info,GrayQuantum,ping_pixels,exception);
11570 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11572 if (logging != MagickFalse && y == 0)
11573 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11574 " Writing GRAY_ALPHA PNG pixels (4)");
11576 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11577 quantum_info,GrayAlphaQuantum,ping_pixels,
11583 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11584 quantum_info,IndexQuantum,ping_pixels,exception);
11586 if (logging != MagickFalse && y <= 2)
11588 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11589 " Writing row of non-gray pixels (4)");
11591 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11592 " ping_pixels[0]=%d,ping_pixels[1]=%d",
11593 (int)ping_pixels[0],(int)ping_pixels[1]);
11596 png_write_row(ping,ping_pixels);
11598 status=SetImageProgress(image,SaveImageTag,
11599 (MagickOffsetType) (pass * image->rows + y),
11600 num_passes * image->rows);
11602 if (status == MagickFalse)
11610 if (quantum_info != (QuantumInfo *) NULL)
11611 quantum_info=DestroyQuantumInfo(quantum_info);
11613 if (logging != MagickFalse)
11615 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11616 " Wrote PNG image data");
11618 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11619 " Width: %.20g",(double) ping_width);
11621 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11622 " Height: %.20g",(double) ping_height);
11624 if (mng_info->write_png_depth)
11626 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11627 " Defined png:bit-depth: %d",mng_info->write_png_depth);
11630 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11631 " PNG bit-depth written: %d",ping_bit_depth);
11633 if (mng_info->write_png_colortype)
11635 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11636 " Defined png:color-type: %d",mng_info->write_png_colortype-1);
11639 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11640 " PNG color-type written: %d",ping_color_type);
11642 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11643 " PNG Interlace method: %d",ping_interlace_method);
11646 Generate text chunks after IDAT.
11648 if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11650 ResetImagePropertyIterator(image);
11651 property=GetNextImageProperty(image);
11652 while (property != (const char *) NULL)
11657 value=GetImageProperty(image,property,exception);
11659 /* Don't write any "png:" or "jpeg:" properties; those are just for
11660 * "identify" or for passing through to another JPEG
11662 if ((LocaleNCompare(property,"png:",4) != 0 &&
11663 LocaleNCompare(property,"jpeg:",5) != 0) &&
11666 /* Suppress density and units if we wrote a pHYs chunk */
11667 (ping_exclude_pHYs != MagickFalse ||
11668 LocaleCompare(property,"density") != 0 ||
11669 LocaleCompare(property,"units") != 0) &&
11671 /* Suppress the IM-generated Date:create and Date:modify */
11672 (ping_exclude_date == MagickFalse ||
11673 LocaleNCompare(property, "Date:",5) != 0))
11675 if (value != (const char *) NULL)
11678 #if PNG_LIBPNG_VER >= 10400
11679 text=(png_textp) png_malloc(ping,
11680 (png_alloc_size_t) sizeof(png_text));
11682 text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11684 text[0].key=(char *) property;
11685 text[0].text=(char *) value;
11686 text[0].text_length=strlen(value);
11688 if (ping_exclude_tEXt != MagickFalse)
11689 text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11691 else if (ping_exclude_zTXt != MagickFalse)
11692 text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11696 text[0].compression=image_info->compression == NoCompression ||
11697 (image_info->compression == UndefinedCompression &&
11698 text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11699 PNG_TEXT_COMPRESSION_zTXt ;
11702 if (logging != MagickFalse)
11704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11705 " Setting up text chunk");
11707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11708 " keyword: '%s'",text[0].key);
11711 png_set_text(ping,ping_info,text,1);
11712 png_free(ping,text);
11715 property=GetNextImageProperty(image);
11719 /* write any PNG-chunk-e profiles */
11720 (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11722 #if defined(exIf_SUPPORTED) || defined(zxIf_SUPPORTED)
11723 /* write exIf profile */
11725 if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11729 ImageProfileIterator
11732 profile_iterator=AllocateImageProfileIterator(image);
11733 if (profile_iterator)
11738 const unsigned char
11744 while (NextImageProfile(profile_iterator,&profile_name,&profile_info,
11745 &profile_length) != MagickFail)
11747 if (LocaleCompare(profile_name,"exif") == 0)
11753 ResetImageProfileIterator(image);
11755 for (name=GetNextImageProfile(image);
11756 name != (const char *) NULL; )
11758 if (LocaleCompare(name,"exif") == 0)
11763 profile=GetImageProfile(image,name);
11765 if (profile != (StringInfo *) NULL)
11778 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11779 " Have eXIf profile");
11783 length=(png_uint_32) profile_length;
11785 ping_profile=CloneStringInfo(profile);
11786 data=GetStringInfoDatum(ping_profile),
11787 length=(png_uint_32) GetStringInfoLength(ping_profile);
11790 #if defined(zxIf_write_SUPPORTED)
11791 /* For now, write uncompressed exIf/eXIf chunk */
11794 #if 0 /* eXIf chunk is registered */
11795 PNGType(chunk,mng_eXIf);
11796 #else /* eXIf chunk not yet registered; write exIf instead */
11797 PNGType(chunk,mng_exIf);
11800 break; /* othewise crashes */
11802 /* skip the "Exif\0\0" JFIF Exif Header ID */
11805 LogPNGChunk(logging,chunk,length);
11806 (void) WriteBlobMSBULong(image,length);
11807 (void) WriteBlob(image,4,chunk);
11808 (void) WriteBlob(image,length,data+6);
11809 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11810 data+6, (uInt) length));
11813 name=GetNextImageProfile(image);
11819 #endif /* exIf_SUPPORTED) || zxIf_SUPPORTED */
11821 if (logging != MagickFalse)
11822 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11823 " Writing PNG end info");
11825 png_write_end(ping,ping_info);
11827 if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11829 if (mng_info->page.x || mng_info->page.y ||
11830 (ping_width != mng_info->page.width) ||
11831 (ping_height != mng_info->page.height))
11837 Write FRAM 4 with clipping boundaries followed by FRAM 1.
11839 (void) WriteBlobMSBULong(image,27L); /* data length=27 */
11840 PNGType(chunk,mng_FRAM);
11841 LogPNGChunk(logging,mng_FRAM,27L);
11843 chunk[5]=0; /* frame name separator (no name) */
11844 chunk[6]=1; /* flag for changing delay, for next frame only */
11845 chunk[7]=0; /* flag for changing frame timeout */
11846 chunk[8]=1; /* flag for changing frame clipping for next frame */
11847 chunk[9]=0; /* flag for changing frame sync_id */
11848 PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11849 chunk[14]=0; /* clipping boundaries delta type */
11850 PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11852 (png_uint_32) (mng_info->page.x + ping_width));
11853 PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11855 (png_uint_32) (mng_info->page.y + ping_height));
11856 (void) WriteBlob(image,31,chunk);
11857 (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11858 mng_info->old_framing_mode=4;
11859 mng_info->framing_mode=1;
11863 mng_info->framing_mode=3;
11865 if (mng_info->write_mng && !mng_info->need_fram &&
11866 ((int) image->dispose == 3))
11867 png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11870 Free PNG resources.
11873 png_destroy_write_struct(&ping,&ping_info);
11875 pixel_info=RelinquishVirtualMemory(pixel_info);
11877 if (ping_have_blob != MagickFalse)
11878 (void) CloseBlob(image);
11880 image_info=DestroyImageInfo(image_info);
11881 image=DestroyImage(image);
11883 /* Store bit depth actually written */
11884 s[0]=(char) ping_bit_depth;
11887 (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11889 if (logging != MagickFalse)
11890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11891 " exit WriteOnePNGImage()");
11893 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11894 UnlockSemaphoreInfo(ping_semaphore);
11897 /* } for navigation to beginning of SETJMP-protected block. Revert to
11898 * Throwing an Exception when an error occurs.
11901 return(MagickTrue);
11902 /* End write one PNG image */
11907 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11911 % W r i t e P N G I m a g e %
11915 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11917 % WritePNGImage() writes a Portable Network Graphics (PNG) or
11918 % Multiple-image Network Graphics (MNG) image file.
11920 % MNG support written by Glenn Randers-Pehrson, glennrp@image...
11922 % The format of the WritePNGImage method is:
11924 % MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11925 % Image *image,ExceptionInfo *exception)
11927 % A description of each parameter follows:
11929 % o image_info: the image info.
11931 % o image: The image.
11933 % o exception: return any errors or warnings in this structure.
11935 % Returns MagickTrue on success, MagickFalse on failure.
11937 % Communicating with the PNG encoder:
11939 % While the datastream written is always in PNG format and normally would
11940 % be given the "png" file extension, this method also writes the following
11941 % pseudo-formats which are subsets of png:
11943 % o PNG8: An 8-bit indexed PNG datastream is written. If the image has
11944 % a depth greater than 8, the depth is reduced. If transparency
11945 % is present, the tRNS chunk must only have values 0 and 255
11946 % (i.e., transparency is binary: fully opaque or fully
11947 % transparent). If other values are present they will be
11948 % 50%-thresholded to binary transparency. If more than 256
11949 % colors are present, they will be quantized to the 4-4-4-1,
11950 % 3-3-3-1, or 3-3-2-1 palette. The underlying RGB color
11951 % of any resulting fully-transparent pixels is changed to
11952 % the image's background color.
11954 % If you want better quantization or dithering of the colors
11955 % or alpha than that, you need to do it before calling the
11956 % PNG encoder. The pixels contain 8-bit indices even if
11957 % they could be represented with 1, 2, or 4 bits. Grayscale
11958 % images will be written as indexed PNG files even though the
11959 % PNG grayscale type might be slightly more efficient. Please
11960 % note that writing to the PNG8 format may result in loss
11961 % of color and alpha data.
11963 % o PNG24: An 8-bit per sample RGB PNG datastream is written. The tRNS
11964 % chunk can be present to convey binary transparency by naming
11965 % one of the colors as transparent. The only loss incurred
11966 % is reduction of sample depth to 8. If the image has more
11967 % than one transparent color, has semitransparent pixels, or
11968 % has an opaque pixel with the same RGB components as the
11969 % transparent color, an image is not written.
11971 % o PNG32: An 8-bit per sample RGBA PNG is written. Partial
11972 % transparency is permitted, i.e., the alpha sample for
11973 % each pixel can have any value from 0 to 255. The alpha
11974 % channel is present even if the image is fully opaque.
11975 % The only loss in data is the reduction of the sample depth
11978 % o PNG48: A 16-bit per sample RGB PNG datastream is written. The tRNS
11979 % chunk can be present to convey binary transparency by naming
11980 % one of the colors as transparent. If the image has more
11981 % than one transparent color, has semitransparent pixels, or
11982 % has an opaque pixel with the same RGB components as the
11983 % transparent color, an image is not written.
11985 % o PNG64: A 16-bit per sample RGBA PNG is written. Partial
11986 % transparency is permitted, i.e., the alpha sample for
11987 % each pixel can have any value from 0 to 65535. The alpha
11988 % channel is present even if the image is fully opaque.
11990 % o PNG00: A PNG that inherits its colortype and bit-depth from the input
11991 % image, if the input was a PNG, is written. If these values
11992 % cannot be found, or if the pixels have been changed in a way
11993 % that makes this impossible, then "PNG00" falls back to the
11994 % regular "PNG" format.
11996 % o -define: For more precise control of the PNG output, you can use the
11997 % Image options "png:bit-depth" and "png:color-type". These
11998 % can be set from the commandline with "-define" and also
11999 % from the application programming interfaces. The options
12000 % are case-independent and are converted to lowercase before
12001 % being passed to this encoder.
12003 % png:color-type can be 0, 2, 3, 4, or 6.
12005 % When png:color-type is 0 (Grayscale), png:bit-depth can
12006 % be 1, 2, 4, 8, or 16.
12008 % When png:color-type is 2 (RGB), png:bit-depth can
12011 % When png:color-type is 3 (Indexed), png:bit-depth can
12012 % be 1, 2, 4, or 8. This refers to the number of bits
12013 % used to store the index. The color samples always have
12014 % bit-depth 8 in indexed PNG files.
12016 % When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12017 % png:bit-depth can be 8 or 16.
12019 % If the image cannot be written without loss with the
12020 % requested bit-depth and color-type, a PNG file will not
12021 % be written, a warning will be issued, and the encoder will
12022 % return MagickFalse.
12024 % Since image encoders should not be responsible for the "heavy lifting",
12025 % the user should make sure that ImageMagick has already reduced the
12026 % image depth and number of colors and limit transparency to binary
12027 % transparency prior to attempting to write the image with depth, color,
12028 % or transparency limitations.
12030 % Note that another definition, "png:bit-depth-written" exists, but it
12031 % is not intended for external use. It is only used internally by the
12032 % PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12034 % It is possible to request that the PNG encoder write previously-formatted
12035 % ancillary chunks in the output PNG file, using the "-profile" commandline
12036 % option as shown below or by setting the profile via a programming
12039 % -profile PNG-chunk-x:<file>
12041 % where x is a location flag and <file> is a file containing the chunk
12042 % name in the first 4 bytes, then a colon (":"), followed by the chunk data.
12043 % This encoder will compute the chunk length and CRC, so those must not
12044 % be included in the file.
12046 % "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
12047 % or "e" (end, i.e., after IDAT). If you want to write multiple chunks
12048 % of the same type, then add a short unique string after the "x" to prevent
12049 % subsequent profiles from overwriting the preceding ones, e.g.,
12051 % -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
12053 % As of version 6.6.6 the following optimizations are always done:
12055 % o 32-bit depth is reduced to 16.
12056 % o 16-bit depth is reduced to 8 if all pixels contain samples whose
12057 % high byte and low byte are identical.
12058 % o Palette is sorted to remove unused entries and to put a
12059 % transparent color first, if BUILD_PNG_PALETTE is defined.
12060 % o Opaque matte channel is removed when writing an indexed PNG.
12061 % o Grayscale images are reduced to 1, 2, or 4 bit depth if
12062 % this can be done without loss and a larger bit depth N was not
12063 % requested via the "-define png:bit-depth=N" option.
12064 % o If matte channel is present but only one transparent color is
12065 % present, RGB+tRNS is written instead of RGBA
12066 % o Opaque matte channel is removed (or added, if color-type 4 or 6
12067 % was requested when converting an opaque image).
12069 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12071 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12072 Image *image,ExceptionInfo *exception)
12091 assert(image_info != (const ImageInfo *) NULL);
12092 assert(image_info->signature == MagickCoreSignature);
12093 assert(image != (Image *) NULL);
12094 assert(image->signature == MagickCoreSignature);
12095 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12096 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12098 Allocate a MngInfo structure.
12100 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12102 if (mng_info == (MngInfo *) NULL)
12103 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12106 Initialize members of the MngInfo structure.
12108 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12109 mng_info->image=image;
12110 mng_info->equal_backgrounds=MagickTrue;
12112 /* See if user has requested a specific PNG subformat */
12114 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12115 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12116 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12117 mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12118 mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12120 value=GetImageOption(image_info,"png:format");
12122 if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12124 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12125 " Format=%s",value);
12127 mng_info->write_png8 = MagickFalse;
12128 mng_info->write_png24 = MagickFalse;
12129 mng_info->write_png32 = MagickFalse;
12130 mng_info->write_png48 = MagickFalse;
12131 mng_info->write_png64 = MagickFalse;
12133 if (LocaleCompare(value,"png8") == 0)
12134 mng_info->write_png8 = MagickTrue;
12136 else if (LocaleCompare(value,"png24") == 0)
12137 mng_info->write_png24 = MagickTrue;
12139 else if (LocaleCompare(value,"png32") == 0)
12140 mng_info->write_png32 = MagickTrue;
12142 else if (LocaleCompare(value,"png48") == 0)
12143 mng_info->write_png48 = MagickTrue;
12145 else if (LocaleCompare(value,"png64") == 0)
12146 mng_info->write_png64 = MagickTrue;
12148 else if ((LocaleCompare(value,"png00") == 0) ||
12149 LocaleCompare(image_info->magick,"PNG00") == 0)
12151 /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12152 value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12154 if (value != (char *) NULL)
12156 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12157 " png00 inherited bit depth=%s",value);
12159 if (LocaleCompare(value,"1") == 0)
12160 mng_info->write_png_depth = 1;
12162 else if (LocaleCompare(value,"2") == 0)
12163 mng_info->write_png_depth = 2;
12165 else if (LocaleCompare(value,"4") == 0)
12166 mng_info->write_png_depth = 4;
12168 else if (LocaleCompare(value,"8") == 0)
12169 mng_info->write_png_depth = 8;
12171 else if (LocaleCompare(value,"16") == 0)
12172 mng_info->write_png_depth = 16;
12175 value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12177 if (value != (char *) NULL)
12179 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12180 " png00 inherited color type=%s",value);
12182 if (LocaleCompare(value,"0") == 0)
12183 mng_info->write_png_colortype = 1;
12185 else if (LocaleCompare(value,"2") == 0)
12186 mng_info->write_png_colortype = 3;
12188 else if (LocaleCompare(value,"3") == 0)
12189 mng_info->write_png_colortype = 4;
12191 else if (LocaleCompare(value,"4") == 0)
12192 mng_info->write_png_colortype = 5;
12194 else if (LocaleCompare(value,"6") == 0)
12195 mng_info->write_png_colortype = 7;
12200 if (mng_info->write_png8)
12202 mng_info->write_png_colortype = /* 3 */ 4;
12203 mng_info->write_png_depth = 8;
12207 if (mng_info->write_png24)
12209 mng_info->write_png_colortype = /* 2 */ 3;
12210 mng_info->write_png_depth = 8;
12213 if (image->alpha_trait != UndefinedPixelTrait)
12214 (void) SetImageType(image,TrueColorAlphaType,exception);
12217 (void) SetImageType(image,TrueColorType,exception);
12219 (void) SyncImage(image,exception);
12222 if (mng_info->write_png32)
12224 mng_info->write_png_colortype = /* 6 */ 7;
12225 mng_info->write_png_depth = 8;
12227 image->alpha_trait = BlendPixelTrait;
12229 (void) SetImageType(image,TrueColorAlphaType,exception);
12230 (void) SyncImage(image,exception);
12233 if (mng_info->write_png48)
12235 mng_info->write_png_colortype = /* 2 */ 3;
12236 mng_info->write_png_depth = 16;
12239 if (image->alpha_trait != UndefinedPixelTrait)
12240 (void) SetImageType(image,TrueColorAlphaType,exception);
12243 (void) SetImageType(image,TrueColorType,exception);
12245 (void) SyncImage(image,exception);
12248 if (mng_info->write_png64)
12250 mng_info->write_png_colortype = /* 6 */ 7;
12251 mng_info->write_png_depth = 16;
12253 image->alpha_trait = BlendPixelTrait;
12255 (void) SetImageType(image,TrueColorAlphaType,exception);
12256 (void) SyncImage(image,exception);
12259 value=GetImageOption(image_info,"png:bit-depth");
12261 if (value != (char *) NULL)
12263 if (LocaleCompare(value,"1") == 0)
12264 mng_info->write_png_depth = 1;
12266 else if (LocaleCompare(value,"2") == 0)
12267 mng_info->write_png_depth = 2;
12269 else if (LocaleCompare(value,"4") == 0)
12270 mng_info->write_png_depth = 4;
12272 else if (LocaleCompare(value,"8") == 0)
12273 mng_info->write_png_depth = 8;
12275 else if (LocaleCompare(value,"16") == 0)
12276 mng_info->write_png_depth = 16;
12279 (void) ThrowMagickException(exception,
12280 GetMagickModule(),CoderWarning,
12281 "ignoring invalid defined png:bit-depth",
12284 if (logging != MagickFalse)
12285 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12286 " png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12289 value=GetImageOption(image_info,"png:color-type");
12291 if (value != (char *) NULL)
12293 /* We must store colortype+1 because 0 is a valid colortype */
12294 if (LocaleCompare(value,"0") == 0)
12295 mng_info->write_png_colortype = 1;
12297 else if (LocaleCompare(value,"1") == 0)
12298 mng_info->write_png_colortype = 2;
12300 else if (LocaleCompare(value,"2") == 0)
12301 mng_info->write_png_colortype = 3;
12303 else if (LocaleCompare(value,"3") == 0)
12304 mng_info->write_png_colortype = 4;
12306 else if (LocaleCompare(value,"4") == 0)
12307 mng_info->write_png_colortype = 5;
12309 else if (LocaleCompare(value,"6") == 0)
12310 mng_info->write_png_colortype = 7;
12313 (void) ThrowMagickException(exception,
12314 GetMagickModule(),CoderWarning,
12315 "ignoring invalid defined png:color-type",
12318 if (logging != MagickFalse)
12319 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12320 " png:color-type=%d was defined.\n",
12321 mng_info->write_png_colortype-1);
12324 /* Check for chunks to be excluded:
12326 * The default is to not exclude any known chunks except for any
12327 * listed in the "unused_chunks" array, above.
12329 * Chunks can be listed for exclusion via a "png:exclude-chunk"
12330 * define (in the image properties or in the image artifacts)
12331 * or via a mng_info member. For convenience, in addition
12332 * to or instead of a comma-separated list of chunks, the
12333 * "exclude-chunk" string can be simply "all" or "none".
12335 * The exclude-chunk define takes priority over the mng_info.
12337 * A "png:include-chunk" define takes priority over both the
12338 * mng_info and the "png:exclude-chunk" define. Like the
12339 * "exclude-chunk" string, it can define "all" or "none" as
12340 * well as a comma-separated list. Chunks that are unknown to
12341 * ImageMagick are always excluded, regardless of their "copy-safe"
12342 * status according to the PNG specification, and even if they
12343 * appear in the "include-chunk" list. Such defines appearing among
12344 * the image options take priority over those found among the image
12347 * Finally, all chunks listed in the "unused_chunks" array are
12348 * automatically excluded, regardless of the other instructions
12351 * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12352 * will not be written and the gAMA chunk will only be written if it
12353 * is not between .45 and .46, or approximately (1.0/2.2).
12355 * If you exclude tRNS and the image has transparency, the colortype
12356 * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12358 * The -strip option causes StripImage() to set the png:include-chunk
12359 * artifact to "none,trns,gama".
12362 mng_info->ping_exclude_bKGD=MagickFalse;
12363 mng_info->ping_exclude_caNv=MagickFalse;
12364 mng_info->ping_exclude_cHRM=MagickFalse;
12365 mng_info->ping_exclude_date=MagickFalse;
12366 mng_info->ping_exclude_eXIf=MagickFalse;
12367 mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12368 mng_info->ping_exclude_gAMA=MagickFalse;
12369 mng_info->ping_exclude_iCCP=MagickFalse;
12370 /* mng_info->ping_exclude_iTXt=MagickFalse; */
12371 mng_info->ping_exclude_oFFs=MagickFalse;
12372 mng_info->ping_exclude_pHYs=MagickFalse;
12373 mng_info->ping_exclude_sRGB=MagickFalse;
12374 mng_info->ping_exclude_tEXt=MagickFalse;
12375 mng_info->ping_exclude_tIME=MagickFalse;
12376 mng_info->ping_exclude_tRNS=MagickFalse;
12377 mng_info->ping_exclude_vpAg=MagickFalse;
12378 mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12379 mng_info->ping_exclude_zTXt=MagickFalse;
12381 mng_info->ping_preserve_colormap=MagickFalse;
12383 value=GetImageOption(image_info,"png:preserve-colormap");
12385 value=GetImageArtifact(image,"png:preserve-colormap");
12387 mng_info->ping_preserve_colormap=MagickTrue;
12389 mng_info->ping_preserve_iCCP=MagickFalse;
12391 value=GetImageOption(image_info,"png:preserve-iCCP");
12393 value=GetImageArtifact(image,"png:preserve-iCCP");
12395 mng_info->ping_preserve_iCCP=MagickTrue;
12397 /* These compression-level, compression-strategy, and compression-filter
12398 * defines take precedence over values from the -quality option.
12400 value=GetImageOption(image_info,"png:compression-level");
12402 value=GetImageArtifact(image,"png:compression-level");
12405 /* We have to add 1 to everything because 0 is a valid input,
12406 * and we want to use 0 (the default) to mean undefined.
12408 if (LocaleCompare(value,"0") == 0)
12409 mng_info->write_png_compression_level = 1;
12411 else if (LocaleCompare(value,"1") == 0)
12412 mng_info->write_png_compression_level = 2;
12414 else if (LocaleCompare(value,"2") == 0)
12415 mng_info->write_png_compression_level = 3;
12417 else if (LocaleCompare(value,"3") == 0)
12418 mng_info->write_png_compression_level = 4;
12420 else if (LocaleCompare(value,"4") == 0)
12421 mng_info->write_png_compression_level = 5;
12423 else if (LocaleCompare(value,"5") == 0)
12424 mng_info->write_png_compression_level = 6;
12426 else if (LocaleCompare(value,"6") == 0)
12427 mng_info->write_png_compression_level = 7;
12429 else if (LocaleCompare(value,"7") == 0)
12430 mng_info->write_png_compression_level = 8;
12432 else if (LocaleCompare(value,"8") == 0)
12433 mng_info->write_png_compression_level = 9;
12435 else if (LocaleCompare(value,"9") == 0)
12436 mng_info->write_png_compression_level = 10;
12439 (void) ThrowMagickException(exception,
12440 GetMagickModule(),CoderWarning,
12441 "ignoring invalid defined png:compression-level",
12445 value=GetImageOption(image_info,"png:compression-strategy");
12447 value=GetImageArtifact(image,"png:compression-strategy");
12450 if (LocaleCompare(value,"0") == 0)
12451 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12453 else if (LocaleCompare(value,"1") == 0)
12454 mng_info->write_png_compression_strategy = Z_FILTERED+1;
12456 else if (LocaleCompare(value,"2") == 0)
12457 mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12459 else if (LocaleCompare(value,"3") == 0)
12460 #ifdef Z_RLE /* Z_RLE was added to zlib-1.2.0 */
12461 mng_info->write_png_compression_strategy = Z_RLE+1;
12463 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12466 else if (LocaleCompare(value,"4") == 0)
12467 #ifdef Z_FIXED /* Z_FIXED was added to zlib-1.2.2.2 */
12468 mng_info->write_png_compression_strategy = Z_FIXED+1;
12470 mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12474 (void) ThrowMagickException(exception,
12475 GetMagickModule(),CoderWarning,
12476 "ignoring invalid defined png:compression-strategy",
12480 value=GetImageOption(image_info,"png:compression-filter");
12482 value=GetImageArtifact(image,"png:compression-filter");
12485 /* To do: combinations of filters allowed by libpng
12486 * masks 0x08 through 0xf8
12488 * Implement this as a comma-separated list of 0,1,2,3,4,5
12489 * where 5 is a special case meaning PNG_ALL_FILTERS.
12492 if (LocaleCompare(value,"0") == 0)
12493 mng_info->write_png_compression_filter = 1;
12495 else if (LocaleCompare(value,"1") == 0)
12496 mng_info->write_png_compression_filter = 2;
12498 else if (LocaleCompare(value,"2") == 0)
12499 mng_info->write_png_compression_filter = 3;
12501 else if (LocaleCompare(value,"3") == 0)
12502 mng_info->write_png_compression_filter = 4;
12504 else if (LocaleCompare(value,"4") == 0)
12505 mng_info->write_png_compression_filter = 5;
12507 else if (LocaleCompare(value,"5") == 0)
12508 mng_info->write_png_compression_filter = 6;
12511 (void) ThrowMagickException(exception,
12512 GetMagickModule(),CoderWarning,
12513 "ignoring invalid defined png:compression-filter",
12517 for (source=0; source<8; source++)
12522 value=GetImageOption(image_info,"png:exclude-chunks");
12525 value=GetImageArtifact(image,"png:exclude-chunks");
12528 value=GetImageOption(image_info,"png:exclude-chunk");
12531 value=GetImageArtifact(image,"png:exclude-chunk");
12534 value=GetImageOption(image_info,"png:include-chunks");
12537 value=GetImageArtifact(image,"png:include-chunks");
12540 value=GetImageOption(image_info,"png:include-chunk");
12543 value=GetImageArtifact(image,"png:include-chunk");
12549 excluding = MagickTrue;
12551 excluding = MagickFalse;
12553 if (logging != MagickFalse)
12555 if (source == 0 || source == 2)
12556 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12557 " png:exclude-chunk=%s found in image options.\n", value);
12558 else if (source == 1 || source == 3)
12559 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12560 " png:exclude-chunk=%s found in image artifacts.\n", value);
12561 else if (source == 4 || source == 6)
12562 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12563 " png:include-chunk=%s found in image options.\n", value);
12564 else /* if (source == 5 || source == 7) */
12565 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12566 " png:include-chunk=%s found in image artifacts.\n", value);
12569 if (IsOptionMember("all",value) != MagickFalse)
12571 mng_info->ping_exclude_bKGD=excluding;
12572 mng_info->ping_exclude_caNv=excluding;
12573 mng_info->ping_exclude_cHRM=excluding;
12574 mng_info->ping_exclude_date=excluding;
12575 mng_info->ping_exclude_EXIF=excluding;
12576 mng_info->ping_exclude_eXIf=excluding;
12577 mng_info->ping_exclude_gAMA=excluding;
12578 mng_info->ping_exclude_iCCP=excluding;
12579 /* mng_info->ping_exclude_iTXt=excluding; */
12580 mng_info->ping_exclude_oFFs=excluding;
12581 mng_info->ping_exclude_pHYs=excluding;
12582 mng_info->ping_exclude_sRGB=excluding;
12583 mng_info->ping_exclude_tEXt=excluding;
12584 mng_info->ping_exclude_tIME=excluding;
12585 mng_info->ping_exclude_tRNS=excluding;
12586 mng_info->ping_exclude_vpAg=excluding;
12587 mng_info->ping_exclude_zCCP=excluding;
12588 mng_info->ping_exclude_zTXt=excluding;
12591 if (IsOptionMember("none",value) != MagickFalse)
12593 mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12595 mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12597 mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12599 mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12601 mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12603 mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12605 mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12607 mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12609 /* mng_info->ping_exclude_iTXt=!excluding; */
12610 mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12612 mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12614 mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12616 mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12618 mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12620 mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12622 mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12624 mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12626 mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12630 if (IsOptionMember("bkgd",value) != MagickFalse)
12631 mng_info->ping_exclude_bKGD=excluding;
12633 if (IsOptionMember("caNv",value) != MagickFalse)
12634 mng_info->ping_exclude_caNv=excluding;
12636 if (IsOptionMember("chrm",value) != MagickFalse)
12637 mng_info->ping_exclude_cHRM=excluding;
12639 if (IsOptionMember("date",value) != MagickFalse)
12640 mng_info->ping_exclude_date=excluding;
12642 if (IsOptionMember("exif",value) != MagickFalse)
12644 mng_info->ping_exclude_EXIF=excluding;
12645 mng_info->ping_exclude_eXIf=excluding;
12648 if (IsOptionMember("gama",value) != MagickFalse)
12649 mng_info->ping_exclude_gAMA=excluding;
12651 if (IsOptionMember("iccp",value) != MagickFalse)
12652 mng_info->ping_exclude_iCCP=excluding;
12655 if (IsOptionMember("itxt",value) != MagickFalse)
12656 mng_info->ping_exclude_iTXt=excluding;
12659 if (IsOptionMember("offs",value) != MagickFalse)
12660 mng_info->ping_exclude_oFFs=excluding;
12662 if (IsOptionMember("phys",value) != MagickFalse)
12663 mng_info->ping_exclude_pHYs=excluding;
12665 if (IsOptionMember("srgb",value) != MagickFalse)
12666 mng_info->ping_exclude_sRGB=excluding;
12668 if (IsOptionMember("text",value) != MagickFalse)
12669 mng_info->ping_exclude_tEXt=excluding;
12671 if (IsOptionMember("time",value) != MagickFalse)
12672 mng_info->ping_exclude_tIME=excluding;
12674 if (IsOptionMember("trns",value) != MagickFalse)
12675 mng_info->ping_exclude_tRNS=excluding;
12677 if (IsOptionMember("vpag",value) != MagickFalse)
12678 mng_info->ping_exclude_vpAg=excluding;
12680 if (IsOptionMember("zccp",value) != MagickFalse)
12681 mng_info->ping_exclude_zCCP=excluding;
12683 if (IsOptionMember("ztxt",value) != MagickFalse)
12684 mng_info->ping_exclude_zTXt=excluding;
12687 if (logging != MagickFalse)
12689 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12690 " Chunks to be excluded from the output png:");
12691 if (mng_info->ping_exclude_bKGD != MagickFalse)
12692 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12694 if (mng_info->ping_exclude_caNv != MagickFalse)
12695 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12697 if (mng_info->ping_exclude_cHRM != MagickFalse)
12698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12700 if (mng_info->ping_exclude_date != MagickFalse)
12701 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12703 if (mng_info->ping_exclude_EXIF != MagickFalse)
12704 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12706 if (mng_info->ping_exclude_eXIf != MagickFalse)
12707 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12709 if (mng_info->ping_exclude_gAMA != MagickFalse)
12710 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12712 if (mng_info->ping_exclude_iCCP != MagickFalse)
12713 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716 if (mng_info->ping_exclude_iTXt != MagickFalse)
12717 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12721 if (mng_info->ping_exclude_oFFs != MagickFalse)
12722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12724 if (mng_info->ping_exclude_pHYs != MagickFalse)
12725 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12727 if (mng_info->ping_exclude_sRGB != MagickFalse)
12728 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12730 if (mng_info->ping_exclude_tEXt != MagickFalse)
12731 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12733 if (mng_info->ping_exclude_tIME != MagickFalse)
12734 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12736 if (mng_info->ping_exclude_tRNS != MagickFalse)
12737 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12739 if (mng_info->ping_exclude_vpAg != MagickFalse)
12740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12742 if (mng_info->ping_exclude_zCCP != MagickFalse)
12743 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12745 if (mng_info->ping_exclude_zTXt != MagickFalse)
12746 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12750 mng_info->need_blob = MagickTrue;
12752 status=WriteOnePNGImage(mng_info,image_info,image,exception);
12754 mng_info=MngInfoFreeStruct(mng_info);
12756 if (logging != MagickFalse)
12757 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12762 #if defined(JNG_SUPPORTED)
12764 /* Write one JNG image */
12765 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12766 const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12787 jng_alpha_compression_method,
12788 jng_alpha_sample_depth,
12796 logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12797 " Enter WriteOneJNGImage()");
12799 blob=(unsigned char *) NULL;
12800 jpeg_image=(Image *) NULL;
12801 jpeg_image_info=(ImageInfo *) NULL;
12805 transparent=image_info->type==GrayscaleAlphaType ||
12806 image_info->type==TrueColorAlphaType ||
12807 image->alpha_trait != UndefinedPixelTrait;
12809 jng_alpha_sample_depth = 0;
12811 jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12813 jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12815 jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12816 image_info->quality;
12818 if (jng_alpha_quality >= 1000)
12819 jng_alpha_quality /= 1000;
12823 if (transparent != 0)
12827 /* Create JPEG blob, image, and image_info */
12828 if (logging != MagickFalse)
12829 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12830 " Creating jpeg_image_info for alpha.");
12832 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12834 if (jpeg_image_info == (ImageInfo *) NULL)
12835 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12837 if (logging != MagickFalse)
12838 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12839 " Creating jpeg_image.");
12841 jpeg_image=SeparateImage(image,AlphaChannel,exception);
12842 if (jpeg_image == (Image *) NULL)
12843 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12844 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12845 jpeg_image->alpha_trait=UndefinedPixelTrait;
12846 jpeg_image->quality=jng_alpha_quality;
12847 jpeg_image_info->type=GrayscaleType;
12848 (void) SetImageType(jpeg_image,GrayscaleType,exception);
12849 (void) AcquireUniqueFilename(jpeg_image->filename);
12850 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12851 "%s",jpeg_image->filename);
12855 jng_alpha_compression_method=0;
12857 jng_alpha_sample_depth=0;
12860 /* To do: check bit depth of PNG alpha channel */
12862 /* Check if image is grayscale. */
12863 if (image_info->type != TrueColorAlphaType && image_info->type !=
12864 TrueColorType && SetImageGray(image,exception))
12867 if (logging != MagickFalse)
12869 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12870 " JNG Quality = %d",(int) jng_quality);
12871 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12872 " JNG Color Type = %d",jng_color_type);
12873 if (transparent != 0)
12875 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12876 " JNG Alpha Compression = %d",jng_alpha_compression_method);
12877 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12878 " JNG Alpha Depth = %d",jng_alpha_sample_depth);
12879 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12880 " JNG Alpha Quality = %d",(int) jng_alpha_quality);
12884 if (transparent != 0)
12886 if (jng_alpha_compression_method==0)
12891 /* Encode alpha as a grayscale PNG blob */
12892 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12894 if (status == MagickFalse)
12895 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12897 if (logging != MagickFalse)
12898 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12899 " Creating PNG blob.");
12901 (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12903 (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12904 jpeg_image_info->interlace=NoInterlace;
12906 /* Exclude all ancillary chunks */
12907 (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12909 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12910 &length,exception);
12912 /* Retrieve sample depth used */
12913 value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12914 if (value != (char *) NULL)
12915 jng_alpha_sample_depth= (unsigned int) value[0];
12919 /* Encode alpha as a grayscale JPEG blob */
12921 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12923 if (status == MagickFalse)
12924 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12926 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12928 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12929 jpeg_image_info->interlace=NoInterlace;
12930 if (logging != MagickFalse)
12931 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12932 " Creating blob.");
12933 blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12934 jpeg_image,&length,
12936 jng_alpha_sample_depth=8;
12938 if (logging != MagickFalse)
12939 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12940 " Successfully read jpeg_image into a blob, length=%.20g.",
12944 /* Destroy JPEG image and image_info */
12945 jpeg_image=DestroyImage(jpeg_image);
12946 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12947 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12950 /* Write JHDR chunk */
12951 (void) WriteBlobMSBULong(image,16L); /* chunk data length=16 */
12952 PNGType(chunk,mng_JHDR);
12953 LogPNGChunk(logging,mng_JHDR,16L);
12954 PNGLong(chunk+4,(png_uint_32) image->columns);
12955 PNGLong(chunk+8,(png_uint_32) image->rows);
12956 chunk[12]=jng_color_type;
12957 chunk[13]=8; /* sample depth */
12958 chunk[14]=8; /*jng_image_compression_method */
12959 chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12960 chunk[16]=jng_alpha_sample_depth;
12961 chunk[17]=jng_alpha_compression_method;
12962 chunk[18]=0; /*jng_alpha_filter_method */
12963 chunk[19]=0; /*jng_alpha_interlace_method */
12964 (void) WriteBlob(image,20,chunk);
12965 (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12966 if (logging != MagickFalse)
12968 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969 " JNG width:%15lu",(unsigned long) image->columns);
12971 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12972 " JNG height:%14lu",(unsigned long) image->rows);
12974 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12975 " JNG color type:%10d",jng_color_type);
12977 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12978 " JNG sample depth:%8d",8);
12980 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12981 " JNG compression:%9d",8);
12983 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984 " JNG interlace:%11d",0);
12986 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12987 " JNG alpha depth:%9d",jng_alpha_sample_depth);
12989 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12990 " JNG alpha compression:%3d",jng_alpha_compression_method);
12992 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12993 " JNG alpha filter:%8d",0);
12995 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12996 " JNG alpha interlace:%5d",0);
12999 /* Write any JNG-chunk-b profiles */
13000 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
13003 Write leading ancillary chunks
13006 if (transparent != 0)
13009 Write JNG bKGD chunk
13020 if (jng_color_type == 8 || jng_color_type == 12)
13024 (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
13025 PNGType(chunk,mng_bKGD);
13026 LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
13027 red=ScaleQuantumToChar(image->background_color.red);
13028 green=ScaleQuantumToChar(image->background_color.green);
13029 blue=ScaleQuantumToChar(image->background_color.blue);
13036 (void) WriteBlob(image,(size_t) num_bytes,chunk);
13037 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13040 if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13043 Write JNG sRGB chunk
13045 (void) WriteBlobMSBULong(image,1L);
13046 PNGType(chunk,mng_sRGB);
13047 LogPNGChunk(logging,mng_sRGB,1L);
13049 if (image->rendering_intent != UndefinedIntent)
13050 chunk[4]=(unsigned char)
13051 Magick_RenderingIntent_to_PNG_RenderingIntent(
13052 (image->rendering_intent));
13055 chunk[4]=(unsigned char)
13056 Magick_RenderingIntent_to_PNG_RenderingIntent(
13057 (PerceptualIntent));
13059 (void) WriteBlob(image,5,chunk);
13060 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13064 if (image->gamma != 0.0)
13067 Write JNG gAMA chunk
13069 (void) WriteBlobMSBULong(image,4L);
13070 PNGType(chunk,mng_gAMA);
13071 LogPNGChunk(logging,mng_gAMA,4L);
13072 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13073 (void) WriteBlob(image,8,chunk);
13074 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13077 if ((mng_info->equal_chrms == MagickFalse) &&
13078 (image->chromaticity.red_primary.x != 0.0))
13084 Write JNG cHRM chunk
13086 (void) WriteBlobMSBULong(image,32L);
13087 PNGType(chunk,mng_cHRM);
13088 LogPNGChunk(logging,mng_cHRM,32L);
13089 primary=image->chromaticity.white_point;
13090 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13091 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13092 primary=image->chromaticity.red_primary;
13093 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13094 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13095 primary=image->chromaticity.green_primary;
13096 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13097 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13098 primary=image->chromaticity.blue_primary;
13099 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13100 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13101 (void) WriteBlob(image,36,chunk);
13102 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13106 if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13109 Write JNG pHYs chunk
13111 (void) WriteBlobMSBULong(image,9L);
13112 PNGType(chunk,mng_pHYs);
13113 LogPNGChunk(logging,mng_pHYs,9L);
13114 if (image->units == PixelsPerInchResolution)
13116 PNGLong(chunk+4,(png_uint_32)
13117 (image->resolution.x*100.0/2.54+0.5));
13119 PNGLong(chunk+8,(png_uint_32)
13120 (image->resolution.y*100.0/2.54+0.5));
13127 if (image->units == PixelsPerCentimeterResolution)
13129 PNGLong(chunk+4,(png_uint_32)
13130 (image->resolution.x*100.0+0.5));
13132 PNGLong(chunk+8,(png_uint_32)
13133 (image->resolution.y*100.0+0.5));
13140 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13141 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13145 (void) WriteBlob(image,13,chunk);
13146 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13149 if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13152 Write JNG oFFs chunk
13154 (void) WriteBlobMSBULong(image,9L);
13155 PNGType(chunk,mng_oFFs);
13156 LogPNGChunk(logging,mng_oFFs,9L);
13157 PNGsLong(chunk+4,(ssize_t) (image->page.x));
13158 PNGsLong(chunk+8,(ssize_t) (image->page.y));
13160 (void) WriteBlob(image,13,chunk);
13161 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13163 if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
13165 (void) WriteBlobMSBULong(image,9L); /* data length=8 */
13166 PNGType(chunk,mng_vpAg);
13167 LogPNGChunk(logging,mng_vpAg,9L);
13168 PNGLong(chunk+4,(png_uint_32) image->page.width);
13169 PNGLong(chunk+8,(png_uint_32) image->page.height);
13170 chunk[12]=0; /* unit = pixels */
13171 (void) WriteBlob(image,13,chunk);
13172 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13175 if (transparent != 0)
13177 if (jng_alpha_compression_method==0)
13185 /* Write IDAT chunk header */
13186 if (logging != MagickFalse)
13187 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13188 " Write IDAT chunks from blob, length=%.20g.",(double)
13191 /* Copy IDAT chunks */
13194 for (i=8; i<(ssize_t) length; i+=len+12)
13196 len=(size_t) (*p) << 24;
13197 len|=(size_t) (*(p+1)) << 16;
13198 len|=(size_t) (*(p+2)) << 8;
13199 len|=(size_t) (*(p+3));
13202 if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13204 /* Found an IDAT chunk. */
13205 (void) WriteBlobMSBULong(image,len);
13206 LogPNGChunk(logging,mng_IDAT,len);
13207 (void) WriteBlob(image,len+4,p);
13208 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13213 if (logging != MagickFalse)
13214 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13215 " Skipping %c%c%c%c chunk, length=%.20g.",
13216 *(p),*(p+1),*(p+2),*(p+3),(double) len);
13221 else if (length != 0)
13223 /* Write JDAA chunk header */
13224 if (logging != MagickFalse)
13225 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13226 " Write JDAA chunk, length=%.20g.",(double) length);
13227 (void) WriteBlobMSBULong(image,(size_t) length);
13228 PNGType(chunk,mng_JDAA);
13229 LogPNGChunk(logging,mng_JDAA,length);
13230 /* Write JDAT chunk(s) data */
13231 (void) WriteBlob(image,4,chunk);
13232 (void) WriteBlob(image,length,blob);
13233 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13236 blob=(unsigned char *) RelinquishMagickMemory(blob);
13239 /* Encode image as a JPEG blob */
13240 if (logging != MagickFalse)
13241 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13242 " Creating jpeg_image_info.");
13243 jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13244 if (jpeg_image_info == (ImageInfo *) NULL)
13245 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13247 if (logging != MagickFalse)
13248 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13249 " Creating jpeg_image.");
13251 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13252 if (jpeg_image == (Image *) NULL)
13253 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13254 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13256 (void) AcquireUniqueFilename(jpeg_image->filename);
13257 (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13258 jpeg_image->filename);
13260 status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13263 if (logging != MagickFalse)
13264 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13265 " Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13266 (double) jpeg_image->rows);
13268 if (status == MagickFalse)
13269 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13271 if (jng_color_type == 8 || jng_color_type == 12)
13272 jpeg_image_info->type=GrayscaleType;
13274 jpeg_image_info->quality=jng_quality;
13275 jpeg_image->quality=jng_quality;
13276 (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13277 (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13279 if (logging != MagickFalse)
13280 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13281 " Creating blob.");
13283 blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13286 if (logging != MagickFalse)
13288 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13289 " Successfully read jpeg_image into a blob, length=%.20g.",
13292 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13293 " Write JDAT chunk, length=%.20g.",(double) length);
13296 /* Write JDAT chunk(s) */
13297 (void) WriteBlobMSBULong(image,(size_t) length);
13298 PNGType(chunk,mng_JDAT);
13299 LogPNGChunk(logging,mng_JDAT,length);
13300 (void) WriteBlob(image,4,chunk);
13301 (void) WriteBlob(image,length,blob);
13302 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13304 jpeg_image=DestroyImage(jpeg_image);
13305 (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13306 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13307 blob=(unsigned char *) RelinquishMagickMemory(blob);
13309 /* Write any JNG-chunk-e profiles */
13310 (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
13312 /* Write IEND chunk */
13313 (void) WriteBlobMSBULong(image,0L);
13314 PNGType(chunk,mng_IEND);
13315 LogPNGChunk(logging,mng_IEND,0);
13316 (void) WriteBlob(image,4,chunk);
13317 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13319 if (logging != MagickFalse)
13320 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13321 " exit WriteOneJNGImage()");
13327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13331 % W r i t e J N G I m a g e %
13335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13337 % WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13339 % JNG support written by Glenn Randers-Pehrson, glennrp@image...
13341 % The format of the WriteJNGImage method is:
13343 % MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13344 % Image *image,ExceptionInfo *exception)
13346 % A description of each parameter follows:
13348 % o image_info: the image info.
13350 % o image: The image.
13352 % o exception: return any errors or warnings in this structure.
13354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13356 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13357 Image *image, ExceptionInfo *exception)
13369 assert(image_info != (const ImageInfo *) NULL);
13370 assert(image_info->signature == MagickCoreSignature);
13371 assert(image != (Image *) NULL);
13372 assert(image->signature == MagickCoreSignature);
13373 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13374 logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13375 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13376 if (status == MagickFalse)
13378 if ((image->columns > 65535UL) || (image->rows > 65535UL))
13379 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13382 Allocate a MngInfo structure.
13384 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13385 if (mng_info == (MngInfo *) NULL)
13386 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13388 Initialize members of the MngInfo structure.
13390 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13391 mng_info->image=image;
13393 (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13395 status=WriteOneJNGImage(mng_info,image_info,image,exception);
13396 mng_info=MngInfoFreeStruct(mng_info);
13397 (void) CloseBlob(image);
13399 (void) CatchImageException(image);
13400 if (logging != MagickFalse)
13401 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13406 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13407 Image *image, ExceptionInfo *exception)
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 mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13475 if (mng_info == (MngInfo *) NULL)
13476 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13478 Initialize members of the MngInfo structure.
13480 (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13481 mng_info->image=image;
13482 write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13485 * See if user has requested a specific PNG subformat to be used
13486 * for all of the PNGs in the MNG being written, e.g.,
13488 * convert *.png png8:animation.mng
13490 * To do: check -define png:bit_depth and png:color_type as well,
13491 * or perhaps use mng:bit_depth and mng:color_type instead for
13495 mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13496 mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13497 mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13499 write_jng=MagickFalse;
13500 if (image_info->compression == JPEGCompression)
13501 write_jng=MagickTrue;
13503 mng_info->adjoin=image_info->adjoin &&
13504 (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13506 if (logging != MagickFalse)
13508 /* Log some info about the input */
13512 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13513 " Checking input image(s)\n"
13514 " Image_info depth: %.20g, Type: %d",
13515 (double) image_info->depth, image_info->type);
13518 for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13521 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13522 " Scene: %.20g\n, Image depth: %.20g",
13523 (double) scene++, (double) p->depth);
13525 if (p->alpha_trait != UndefinedPixelTrait)
13526 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13530 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13533 if (p->storage_class == PseudoClass)
13534 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13535 " Storage class: PseudoClass");
13538 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13539 " Storage class: DirectClass");
13542 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13543 " Number of colors: %.20g",(double) p->colors);
13546 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13547 " Number of colors: unspecified");
13549 if (mng_info->adjoin == MagickFalse)
13554 use_global_plte=MagickFalse;
13555 all_images_are_gray=MagickFalse;
13556 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13557 need_local_plte=MagickTrue;
13559 need_defi=MagickFalse;
13560 need_matte=MagickFalse;
13561 mng_info->framing_mode=1;
13562 mng_info->old_framing_mode=1;
13565 if (image_info->page != (char *) NULL)
13568 Determine image bounding box.
13570 SetGeometry(image,&mng_info->page);
13571 (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13572 &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13587 mng_info->page=image->page;
13588 need_geom=MagickTrue;
13589 if (mng_info->page.width || mng_info->page.height)
13590 need_geom=MagickFalse;
13592 Check all the scenes.
13594 initial_delay=image->delay;
13595 need_iterations=MagickFalse;
13596 mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13597 mng_info->equal_physs=MagickTrue,
13598 mng_info->equal_gammas=MagickTrue;
13599 mng_info->equal_srgbs=MagickTrue;
13600 mng_info->equal_backgrounds=MagickTrue;
13602 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13603 defined(PNG_MNG_FEATURES_SUPPORTED)
13604 all_images_are_gray=MagickTrue;
13605 mng_info->equal_palettes=MagickFalse;
13606 need_local_plte=MagickFalse;
13608 for (next_image=image; next_image != (Image *) NULL; )
13612 if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13613 mng_info->page.width=next_image->columns+next_image->page.x;
13615 if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13616 mng_info->page.height=next_image->rows+next_image->page.y;
13619 if (next_image->page.x || next_image->page.y)
13620 need_defi=MagickTrue;
13622 if (next_image->alpha_trait != UndefinedPixelTrait)
13623 need_matte=MagickTrue;
13625 if ((int) next_image->dispose >= BackgroundDispose)
13626 if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13627 next_image->page.x || next_image->page.y ||
13628 ((next_image->columns < mng_info->page.width) &&
13629 (next_image->rows < mng_info->page.height)))
13630 mng_info->need_fram=MagickTrue;
13632 if (next_image->iterations)
13633 need_iterations=MagickTrue;
13635 final_delay=next_image->delay;
13637 if (final_delay != initial_delay || final_delay > 1UL*
13638 next_image->ticks_per_second)
13639 mng_info->need_fram=1;
13641 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13642 defined(PNG_MNG_FEATURES_SUPPORTED)
13644 check for global palette possibility.
13646 if (image->alpha_trait != UndefinedPixelTrait)
13647 need_local_plte=MagickTrue;
13649 if (need_local_plte == 0)
13651 if (SetImageGray(image,exception) == MagickFalse)
13652 all_images_are_gray=MagickFalse;
13653 mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13654 if (use_global_plte == 0)
13655 use_global_plte=mng_info->equal_palettes;
13656 need_local_plte=!mng_info->equal_palettes;
13659 if (GetNextImageInList(next_image) != (Image *) NULL)
13661 if (next_image->background_color.red !=
13662 next_image->next->background_color.red ||
13663 next_image->background_color.green !=
13664 next_image->next->background_color.green ||
13665 next_image->background_color.blue !=
13666 next_image->next->background_color.blue)
13667 mng_info->equal_backgrounds=MagickFalse;
13669 if (next_image->gamma != next_image->next->gamma)
13670 mng_info->equal_gammas=MagickFalse;
13672 if (next_image->rendering_intent !=
13673 next_image->next->rendering_intent)
13674 mng_info->equal_srgbs=MagickFalse;
13676 if ((next_image->units != next_image->next->units) ||
13677 (next_image->resolution.x != next_image->next->resolution.x) ||
13678 (next_image->resolution.y != next_image->next->resolution.y))
13679 mng_info->equal_physs=MagickFalse;
13681 if (mng_info->equal_chrms)
13683 if (next_image->chromaticity.red_primary.x !=
13684 next_image->next->chromaticity.red_primary.x ||
13685 next_image->chromaticity.red_primary.y !=
13686 next_image->next->chromaticity.red_primary.y ||
13687 next_image->chromaticity.green_primary.x !=
13688 next_image->next->chromaticity.green_primary.x ||
13689 next_image->chromaticity.green_primary.y !=
13690 next_image->next->chromaticity.green_primary.y ||
13691 next_image->chromaticity.blue_primary.x !=
13692 next_image->next->chromaticity.blue_primary.x ||
13693 next_image->chromaticity.blue_primary.y !=
13694 next_image->next->chromaticity.blue_primary.y ||
13695 next_image->chromaticity.white_point.x !=
13696 next_image->next->chromaticity.white_point.x ||
13697 next_image->chromaticity.white_point.y !=
13698 next_image->next->chromaticity.white_point.y)
13699 mng_info->equal_chrms=MagickFalse;
13703 next_image=GetNextImageInList(next_image);
13705 if (image_count < 2)
13707 mng_info->equal_backgrounds=MagickFalse;
13708 mng_info->equal_chrms=MagickFalse;
13709 mng_info->equal_gammas=MagickFalse;
13710 mng_info->equal_srgbs=MagickFalse;
13711 mng_info->equal_physs=MagickFalse;
13712 use_global_plte=MagickFalse;
13713 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13714 need_local_plte=MagickTrue;
13716 need_iterations=MagickFalse;
13719 if (mng_info->need_fram == MagickFalse)
13722 Only certain framing rates 100/n are exactly representable without
13723 the FRAM chunk but we'll allow some slop in VLC files
13725 if (final_delay == 0)
13727 if (need_iterations != MagickFalse)
13730 It's probably a GIF with loop; don't run it *too* fast.
13732 if (mng_info->adjoin)
13735 (void) ThrowMagickException(exception,GetMagickModule(),
13737 "input has zero delay between all frames; assuming",
13742 mng_info->ticks_per_second=0;
13744 if (final_delay != 0)
13745 mng_info->ticks_per_second=(png_uint_32)
13746 (image->ticks_per_second/final_delay);
13747 if (final_delay > 50)
13748 mng_info->ticks_per_second=2;
13750 if (final_delay > 75)
13751 mng_info->ticks_per_second=1;
13753 if (final_delay > 125)
13754 mng_info->need_fram=MagickTrue;
13756 if (need_defi && final_delay > 2 && (final_delay != 4) &&
13757 (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13758 (final_delay != 25) && (final_delay != 50) &&
13759 (final_delay != (size_t) image->ticks_per_second))
13760 mng_info->need_fram=MagickTrue; /* make it exact; cannot be VLC */
13763 if (mng_info->need_fram != MagickFalse)
13764 mng_info->ticks_per_second=image->ticks_per_second;
13766 If pseudocolor, we should also check to see if all the
13767 palettes are identical and write a global PLTE if they are.
13771 Write the MNG version 1.0 signature and MHDR chunk.
13773 (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13774 (void) WriteBlobMSBULong(image,28L); /* chunk data length=28 */
13775 PNGType(chunk,mng_MHDR);
13776 LogPNGChunk(logging,mng_MHDR,28L);
13777 PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13778 PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13779 PNGLong(chunk+12,mng_info->ticks_per_second);
13780 PNGLong(chunk+16,0L); /* layer count=unknown */
13781 PNGLong(chunk+20,0L); /* frame count=unknown */
13782 PNGLong(chunk+24,0L); /* play time=unknown */
13787 if (need_defi || mng_info->need_fram || use_global_plte)
13788 PNGLong(chunk+28,27L); /* simplicity=LC+JNG */
13791 PNGLong(chunk+28,25L); /* simplicity=VLC+JNG */
13796 if (need_defi || mng_info->need_fram || use_global_plte)
13797 PNGLong(chunk+28,19L); /* simplicity=LC+JNG, no transparency */
13800 PNGLong(chunk+28,17L); /* simplicity=VLC+JNG, no transparency */
13808 if (need_defi || mng_info->need_fram || use_global_plte)
13809 PNGLong(chunk+28,11L); /* simplicity=LC */
13812 PNGLong(chunk+28,9L); /* simplicity=VLC */
13817 if (need_defi || mng_info->need_fram || use_global_plte)
13818 PNGLong(chunk+28,3L); /* simplicity=LC, no transparency */
13821 PNGLong(chunk+28,1L); /* simplicity=VLC, no transparency */
13824 (void) WriteBlob(image,32,chunk);
13825 (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13826 option=GetImageOption(image_info,"mng:need-cacheoff");
13827 if (option != (const char *) NULL)
13832 Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13834 PNGType(chunk,mng_nEED);
13835 length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13836 (void) WriteBlobMSBULong(image,(size_t) length);
13837 LogPNGChunk(logging,mng_nEED,(size_t) length);
13839 (void) WriteBlob(image,length,chunk);
13840 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13842 if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13843 (GetNextImageInList(image) != (Image *) NULL) &&
13844 (image->iterations != 1))
13847 Write MNG TERM chunk
13849 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
13850 PNGType(chunk,mng_TERM);
13851 LogPNGChunk(logging,mng_TERM,10L);
13852 chunk[4]=3; /* repeat animation */
13853 chunk[5]=0; /* show last frame when done */
13854 PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13855 final_delay/MagickMax(image->ticks_per_second,1)));
13857 if (image->iterations == 0)
13858 PNGLong(chunk+10,PNG_UINT_31_MAX);
13861 PNGLong(chunk+10,(png_uint_32) image->iterations);
13863 if (logging != MagickFalse)
13865 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13866 " TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13867 final_delay/MagickMax(image->ticks_per_second,1)));
13869 if (image->iterations == 0)
13870 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13871 " TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13874 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13875 " Image iterations: %.20g",(double) image->iterations);
13877 (void) WriteBlob(image,14,chunk);
13878 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13881 To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13883 if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13884 mng_info->equal_srgbs)
13887 Write MNG sRGB chunk
13889 (void) WriteBlobMSBULong(image,1L);
13890 PNGType(chunk,mng_sRGB);
13891 LogPNGChunk(logging,mng_sRGB,1L);
13893 if (image->rendering_intent != UndefinedIntent)
13894 chunk[4]=(unsigned char)
13895 Magick_RenderingIntent_to_PNG_RenderingIntent(
13896 (image->rendering_intent));
13899 chunk[4]=(unsigned char)
13900 Magick_RenderingIntent_to_PNG_RenderingIntent(
13901 (PerceptualIntent));
13903 (void) WriteBlob(image,5,chunk);
13904 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13905 mng_info->have_write_global_srgb=MagickTrue;
13910 if (image->gamma && mng_info->equal_gammas)
13913 Write MNG gAMA chunk
13915 (void) WriteBlobMSBULong(image,4L);
13916 PNGType(chunk,mng_gAMA);
13917 LogPNGChunk(logging,mng_gAMA,4L);
13918 PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13919 (void) WriteBlob(image,8,chunk);
13920 (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13921 mng_info->have_write_global_gama=MagickTrue;
13923 if (mng_info->equal_chrms)
13929 Write MNG cHRM chunk
13931 (void) WriteBlobMSBULong(image,32L);
13932 PNGType(chunk,mng_cHRM);
13933 LogPNGChunk(logging,mng_cHRM,32L);
13934 primary=image->chromaticity.white_point;
13935 PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13936 PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13937 primary=image->chromaticity.red_primary;
13938 PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13939 PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13940 primary=image->chromaticity.green_primary;
13941 PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13942 PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13943 primary=image->chromaticity.blue_primary;
13944 PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13945 PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13946 (void) WriteBlob(image,36,chunk);
13947 (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13948 mng_info->have_write_global_chrm=MagickTrue;
13951 if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13954 Write MNG pHYs chunk
13956 (void) WriteBlobMSBULong(image,9L);
13957 PNGType(chunk,mng_pHYs);
13958 LogPNGChunk(logging,mng_pHYs,9L);
13960 if (image->units == PixelsPerInchResolution)
13962 PNGLong(chunk+4,(png_uint_32)
13963 (image->resolution.x*100.0/2.54+0.5));
13965 PNGLong(chunk+8,(png_uint_32)
13966 (image->resolution.y*100.0/2.54+0.5));
13973 if (image->units == PixelsPerCentimeterResolution)
13975 PNGLong(chunk+4,(png_uint_32)
13976 (image->resolution.x*100.0+0.5));
13978 PNGLong(chunk+8,(png_uint_32)
13979 (image->resolution.y*100.0+0.5));
13986 PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13987 PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13991 (void) WriteBlob(image,13,chunk);
13992 (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13995 Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13996 or does not cover the entire frame.
13998 if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13999 image->page.x > 0 || image->page.y > 0 || (image->page.width &&
14000 (image->page.width+image->page.x < mng_info->page.width))
14001 || (image->page.height && (image->page.height+image->page.y
14002 < mng_info->page.height))))
14004 (void) WriteBlobMSBULong(image,6L);
14005 PNGType(chunk,mng_BACK);
14006 LogPNGChunk(logging,mng_BACK,6L);
14007 red=ScaleQuantumToShort(image->background_color.red);
14008 green=ScaleQuantumToShort(image->background_color.green);
14009 blue=ScaleQuantumToShort(image->background_color.blue);
14010 PNGShort(chunk+4,red);
14011 PNGShort(chunk+6,green);
14012 PNGShort(chunk+8,blue);
14013 (void) WriteBlob(image,10,chunk);
14014 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14015 if (mng_info->equal_backgrounds)
14017 (void) WriteBlobMSBULong(image,6L);
14018 PNGType(chunk,mng_bKGD);
14019 LogPNGChunk(logging,mng_bKGD,6L);
14020 (void) WriteBlob(image,10,chunk);
14021 (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14025 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
14026 if ((need_local_plte == MagickFalse) &&
14027 (image->storage_class == PseudoClass) &&
14028 (all_images_are_gray == MagickFalse))
14034 Write MNG PLTE chunk
14036 data_length=3*image->colors;
14037 (void) WriteBlobMSBULong(image,data_length);
14038 PNGType(chunk,mng_PLTE);
14039 LogPNGChunk(logging,mng_PLTE,data_length);
14041 for (i=0; i < (ssize_t) image->colors; i++)
14043 chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14044 image->colormap[i].red) & 0xff);
14045 chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14046 image->colormap[i].green) & 0xff);
14047 chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14048 image->colormap[i].blue) & 0xff);
14051 (void) WriteBlob(image,data_length+4,chunk);
14052 (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14053 mng_info->have_write_global_plte=MagickTrue;
14059 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14060 defined(PNG_MNG_FEATURES_SUPPORTED)
14061 mng_info->equal_palettes=MagickFalse;
14065 if (mng_info->adjoin)
14067 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14068 defined(PNG_MNG_FEATURES_SUPPORTED)
14070 If we aren't using a global palette for the entire MNG, check to
14071 see if we can use one for two or more consecutive images.
14073 if (need_local_plte && use_global_plte && !all_images_are_gray)
14075 if (mng_info->IsPalette)
14078 When equal_palettes is true, this image has the same palette
14079 as the previous PseudoClass image
14081 mng_info->have_write_global_plte=mng_info->equal_palettes;
14082 mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14083 if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14086 Write MNG PLTE chunk
14091 data_length=3*image->colors;
14092 (void) WriteBlobMSBULong(image,data_length);
14093 PNGType(chunk,mng_PLTE);
14094 LogPNGChunk(logging,mng_PLTE,data_length);
14096 for (i=0; i < (ssize_t) image->colors; i++)
14098 chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14099 chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14100 chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14103 (void) WriteBlob(image,data_length+4,chunk);
14104 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14105 (uInt) (data_length+4)));
14106 mng_info->have_write_global_plte=MagickTrue;
14110 mng_info->have_write_global_plte=MagickFalse;
14121 previous_x=mng_info->page.x;
14122 previous_y=mng_info->page.y;
14129 mng_info->page=image->page;
14130 if ((mng_info->page.x != previous_x) ||
14131 (mng_info->page.y != previous_y))
14133 (void) WriteBlobMSBULong(image,12L); /* data length=12 */
14134 PNGType(chunk,mng_DEFI);
14135 LogPNGChunk(logging,mng_DEFI,12L);
14136 chunk[4]=0; /* object 0 MSB */
14137 chunk[5]=0; /* object 0 LSB */
14138 chunk[6]=0; /* visible */
14139 chunk[7]=0; /* abstract */
14140 PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14141 PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14142 (void) WriteBlob(image,16,chunk);
14143 (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14148 mng_info->write_mng=write_mng;
14150 if ((int) image->dispose >= 3)
14151 mng_info->framing_mode=3;
14153 if (mng_info->need_fram && mng_info->adjoin &&
14154 ((image->delay != mng_info->delay) ||
14155 (mng_info->framing_mode != mng_info->old_framing_mode)))
14157 if (image->delay == mng_info->delay)
14160 Write a MNG FRAM chunk with the new framing mode.
14162 (void) WriteBlobMSBULong(image,1L); /* data length=1 */
14163 PNGType(chunk,mng_FRAM);
14164 LogPNGChunk(logging,mng_FRAM,1L);
14165 chunk[4]=(unsigned char) mng_info->framing_mode;
14166 (void) WriteBlob(image,5,chunk);
14167 (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14172 Write a MNG FRAM chunk with the delay.
14174 (void) WriteBlobMSBULong(image,10L); /* data length=10 */
14175 PNGType(chunk,mng_FRAM);
14176 LogPNGChunk(logging,mng_FRAM,10L);
14177 chunk[4]=(unsigned char) mng_info->framing_mode;
14178 chunk[5]=0; /* frame name separator (no name) */
14179 chunk[6]=2; /* flag for changing default delay */
14180 chunk[7]=0; /* flag for changing frame timeout */
14181 chunk[8]=0; /* flag for changing frame clipping */
14182 chunk[9]=0; /* flag for changing frame sync_id */
14183 PNGLong(chunk+10,(png_uint_32)
14184 ((mng_info->ticks_per_second*
14185 image->delay)/MagickMax(image->ticks_per_second,1)));
14186 (void) WriteBlob(image,14,chunk);
14187 (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14188 mng_info->delay=(png_uint_32) image->delay;
14190 mng_info->old_framing_mode=mng_info->framing_mode;
14193 #if defined(JNG_SUPPORTED)
14194 if (image_info->compression == JPEGCompression)
14199 if (logging != MagickFalse)
14200 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14201 " Writing JNG object.");
14202 /* To do: specify the desired alpha compression method. */
14203 write_info=CloneImageInfo(image_info);
14204 write_info->compression=UndefinedCompression;
14205 status=WriteOneJNGImage(mng_info,write_info,image,exception);
14206 write_info=DestroyImageInfo(write_info);
14211 if (logging != MagickFalse)
14212 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14213 " Writing PNG object.");
14215 mng_info->need_blob = MagickFalse;
14216 mng_info->ping_preserve_colormap = MagickFalse;
14218 /* We don't want any ancillary chunks written */
14219 mng_info->ping_exclude_bKGD=MagickTrue;
14220 mng_info->ping_exclude_caNv=MagickTrue;
14221 mng_info->ping_exclude_cHRM=MagickTrue;
14222 mng_info->ping_exclude_date=MagickTrue;
14223 mng_info->ping_exclude_EXIF=MagickTrue;
14224 mng_info->ping_exclude_gAMA=MagickTrue;
14225 mng_info->ping_exclude_iCCP=MagickTrue;
14226 /* mng_info->ping_exclude_iTXt=MagickTrue; */
14227 mng_info->ping_exclude_oFFs=MagickTrue;
14228 mng_info->ping_exclude_pHYs=MagickTrue;
14229 mng_info->ping_exclude_sRGB=MagickTrue;
14230 mng_info->ping_exclude_tEXt=MagickTrue;
14231 mng_info->ping_exclude_tRNS=MagickTrue;
14232 mng_info->ping_exclude_vpAg=MagickTrue;
14233 mng_info->ping_exclude_zCCP=MagickTrue;
14234 mng_info->ping_exclude_zTXt=MagickTrue;
14236 status=WriteOnePNGImage(mng_info,image_info,image,exception);
14239 if (status == MagickFalse)
14241 mng_info=MngInfoFreeStruct(mng_info);
14242 (void) CloseBlob(image);
14243 return(MagickFalse);
14245 (void) CatchImageException(image);
14246 if (GetNextImageInList(image) == (Image *) NULL)
14248 image=SyncNextImageInList(image);
14249 status=SetImageProgress(image,SaveImagesTag,scene++,
14250 GetImageListLength(image));
14252 if (status == MagickFalse)
14255 } while (mng_info->adjoin);
14259 while (GetPreviousImageInList(image) != (Image *) NULL)
14260 image=GetPreviousImageInList(image);
14262 Write the MEND chunk.
14264 (void) WriteBlobMSBULong(image,0x00000000L);
14265 PNGType(chunk,mng_MEND);
14266 LogPNGChunk(logging,mng_MEND,0L);
14267 (void) WriteBlob(image,4,chunk);
14268 (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14271 Relinquish resources.
14273 (void) CloseBlob(image);
14274 mng_info=MngInfoFreeStruct(mng_info);
14276 if (logging != MagickFalse)
14277 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14279 return(MagickTrue);
14281 #else /* PNG_LIBPNG_VER > 10011 */
14283 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14287 printf("Your PNG library is too old: You have libpng-%s\n",
14288 PNG_LIBPNG_VER_STRING);
14290 ThrowBinaryException(CoderError,"PNG library is too old",
14291 image_info->filename);
14294 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14297 return(WritePNGImage(image_info,image));
14299 #endif /* PNG_LIBPNG_VER > 10011 */