• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                            PPPP   N   N   GGGG                              %
7 %                            P   P  NN  N  G                                  %
8 %                            PPPP   N N N  G  GG                              %
9 %                            P      N  NN  G   G                              %
10 %                            P      N   N   GGG                               %
11 %                                                                             %
12 %                                                                             %
13 %              Read/Write Portable Network Graphics Image Format              %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                           Glenn Randers-Pehrson                             %
18 %                               November 1997                                 %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    https://imagemagick.org/script/license.php                               %
28 %                                                                             %
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.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 
40 #define IM
41 
42 
43 /*
44   Include declarations.
45 */
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colormap-private.h"
57 #include "MagickCore/colorspace.h"
58 #include "MagickCore/colorspace-private.h"
59 #include "MagickCore/constitute.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/geometry.h"
64 #include "MagickCore/histogram.h"
65 #include "MagickCore/image.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/layer.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/magick-private.h"
72 #include "MagickCore/memory_.h"
73 #include "MagickCore/memory-private.h"
74 #include "MagickCore/module.h"
75 #include "MagickCore/monitor.h"
76 #include "MagickCore/monitor-private.h"
77 #include "MagickCore/option.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/profile.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resource_.h"
84 #include "MagickCore/semaphore.h"
85 #include "MagickCore/quantum-private.h"
86 #include "MagickCore/static.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/transform.h"
91 #include "MagickCore/utility.h"
92 #if defined(MAGICKCORE_PNG_DELEGATE)
93 
94 /* Suppress libpng pedantic warnings that were added in
95  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
96  * migration to libpng-1.5, remove these defines and then
97  * fix any code that generates warnings.
98  */
99 /* #define PNG_DEPRECATED   Use of this function is deprecated */
100 /* #define PNG_USE_RESULT   The result of this function must be checked */
101 /* #define PNG_NORETURN     This function does not return */
102 /* #define PNG_ALLOCATED    The result of the function is new memory */
103 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
104 
105 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
106 #define PNG_PTR_NORETURN
107 
108 #include <png.h>
109 #include <zlib.h>
110 
111 /* ImageMagick differences */
112 #define first_scene scene
113 
114 #if PNG_LIBPNG_VER > 10011
115 /*
116   Optional declarations. Define or undefine them as you like.
117 */
118 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
119 
120 /*
121   Features under construction.  Define these to work on them.
122 */
123 #undef MNG_OBJECT_BUFFERS
124 #undef MNG_BASI_SUPPORTED
125 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
126 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
127 #if defined(MAGICKCORE_JPEG_DELEGATE)
128 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
129 #endif
130 #if !defined(RGBColorMatchExact)
131 #define IsPNGColorEqual(color,target) \
132        (((color).red == (target).red) && \
133         ((color).green == (target).green) && \
134         ((color).blue == (target).blue))
135 #endif
136 
137 /* Table of recognized sRGB ICC profiles */
138 struct sRGB_info_struct
139 {
140     png_uint_32 len;
141     png_uint_32 crc;
142     png_byte intent;
143 };
144 
145 const struct sRGB_info_struct sRGB_info[] =
146 {
147     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
148     { 3048, 0x3b8772b9UL, 0},
149 
150     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
151     { 3052, 0x427ebb21UL, 1},
152 
153     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
154     {60988, 0x306fd8aeUL, 0},
155 
156     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
157      {60960, 0xbbef7812UL, 0},
158 
159     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
160      { 3024, 0x5d5129ceUL, 1},
161 
162      /* HP-Microsoft sRGB v2 perceptual */
163      { 3144, 0x182ea552UL, 0},
164 
165      /* HP-Microsoft sRGB v2 media-relative */
166      { 3144, 0xf29e526dUL, 1},
167 
168      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
169      {  524, 0xd4938c39UL, 0},
170 
171      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
172      { 3212, 0x034af5a1UL, 0},
173 
174      /* Not recognized */
175      {    0, 0x00000000UL, 0},
176 };
177 
178 /* Macros for left-bit-replication to ensure that pixels
179  * and PixelInfos all have the same image->depth, and for use
180  * in PNG8 quantization.
181  */
182 
183 /* LBR01: Replicate top bit */
184 
185 #define LBR01PacketRed(pixelpacket) \
186      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
187         0 : QuantumRange);
188 
189 #define LBR01PacketGreen(pixelpacket) \
190      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
191         0 : QuantumRange);
192 
193 #define LBR01PacketBlue(pixelpacket) \
194      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
195         0 : QuantumRange);
196 
197 #define LBR01PacketAlpha(pixelpacket) \
198      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
199         0 : QuantumRange);
200 
201 #define LBR01PacketRGB(pixelpacket) \
202         { \
203         LBR01PacketRed((pixelpacket)); \
204         LBR01PacketGreen((pixelpacket)); \
205         LBR01PacketBlue((pixelpacket)); \
206         }
207 
208 #define LBR01PacketRGBA(pixelpacket) \
209         { \
210         LBR01PacketRGB((pixelpacket)); \
211         LBR01PacketAlpha((pixelpacket)); \
212         }
213 
214 #define LBR01PixelRed(pixel) \
215         (SetPixelRed(image, \
216         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
217         0 : QuantumRange,(pixel)));
218 
219 #define LBR01PixelGreen(pixel) \
220         (SetPixelGreen(image, \
221         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
222         0 : QuantumRange,(pixel)));
223 
224 #define LBR01PixelBlue(pixel) \
225         (SetPixelBlue(image, \
226         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
227         0 : QuantumRange,(pixel)));
228 
229 #define LBR01PixelAlpha(pixel) \
230         (SetPixelAlpha(image, \
231         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
232         0 : QuantumRange,(pixel)));
233 
234 #define LBR01PixelRGB(pixel) \
235         { \
236         LBR01PixelRed((pixel)); \
237         LBR01PixelGreen((pixel)); \
238         LBR01PixelBlue((pixel)); \
239         }
240 
241 #define LBR01PixelRGBA(pixel) \
242         { \
243         LBR01PixelRGB((pixel)); \
244         LBR01PixelAlpha((pixel)); \
245         }
246 
247 /* LBR02: Replicate top 2 bits */
248 
249 #define LBR02PacketRed(pixelpacket) \
250    { \
251      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
252      (pixelpacket).red=ScaleCharToQuantum( \
253        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
254    }
255 #define LBR02PacketGreen(pixelpacket) \
256    { \
257      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
258      (pixelpacket).green=ScaleCharToQuantum( \
259        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
260    }
261 #define LBR02PacketBlue(pixelpacket) \
262    { \
263      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
264      (pixelpacket).blue=ScaleCharToQuantum( \
265        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
266    }
267 #define LBR02PacketAlpha(pixelpacket) \
268    { \
269      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
270      (pixelpacket).alpha=ScaleCharToQuantum( \
271        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
272    }
273 
274 #define LBR02PacketRGB(pixelpacket) \
275         { \
276         LBR02PacketRed((pixelpacket)); \
277         LBR02PacketGreen((pixelpacket)); \
278         LBR02PacketBlue((pixelpacket)); \
279         }
280 
281 #define LBR02PacketRGBA(pixelpacket) \
282         { \
283         LBR02PacketRGB((pixelpacket)); \
284         LBR02PacketAlpha((pixelpacket)); \
285         }
286 
287 #define LBR02PixelRed(pixel) \
288    { \
289      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
290        & 0xc0; \
291      SetPixelRed(image, ScaleCharToQuantum( \
292        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
293        (pixel)); \
294    }
295 #define LBR02PixelGreen(pixel) \
296    { \
297      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
298        & 0xc0; \
299      SetPixelGreen(image, ScaleCharToQuantum( \
300        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
301        (pixel)); \
302    }
303 #define LBR02PixelBlue(pixel) \
304    { \
305      unsigned char lbr_bits= \
306        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
307      SetPixelBlue(image, ScaleCharToQuantum( \
308        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
309        (pixel)); \
310    }
311 #define LBR02PixelAlpha(pixel) \
312    { \
313      unsigned char lbr_bits= \
314        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
315      SetPixelAlpha(image, ScaleCharToQuantum( \
316        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
317        (pixel) ); \
318    }
319 
320 #define LBR02PixelRGB(pixel) \
321         { \
322         LBR02PixelRed((pixel)); \
323         LBR02PixelGreen((pixel)); \
324         LBR02PixelBlue((pixel)); \
325         }
326 
327 #define LBR02PixelRGBA(pixel) \
328         { \
329         LBR02PixelRGB((pixel)); \
330         LBR02PixelAlpha((pixel)); \
331         }
332 
333 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
334    PNG8 quantization) */
335 
336 #define LBR03PacketRed(pixelpacket) \
337    { \
338      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
339      (pixelpacket).red=ScaleCharToQuantum( \
340        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
341    }
342 #define LBR03PacketGreen(pixelpacket) \
343    { \
344      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
345      (pixelpacket).green=ScaleCharToQuantum( \
346        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
347    }
348 #define LBR03PacketBlue(pixelpacket) \
349    { \
350      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
351      (pixelpacket).blue=ScaleCharToQuantum( \
352        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
353    }
354 
355 #define LBR03PacketRGB(pixelpacket) \
356         { \
357         LBR03PacketRed((pixelpacket)); \
358         LBR03PacketGreen((pixelpacket)); \
359         LBR03PacketBlue((pixelpacket)); \
360         }
361 
362 #define LBR03PixelRed(pixel) \
363    { \
364      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
365        & 0xe0; \
366      SetPixelRed(image, ScaleCharToQuantum( \
367        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
368    }
369 #define LBR03Green(pixel) \
370    { \
371      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
372        & 0xe0; \
373      SetPixelGreen(image, ScaleCharToQuantum( \
374        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
375    }
376 #define LBR03Blue(pixel) \
377    { \
378      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
379        & 0xe0; \
380      SetPixelBlue(image, ScaleCharToQuantum( \
381        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
382    }
383 
384 #define LBR03RGB(pixel) \
385         { \
386         LBR03PixelRed((pixel)); \
387         LBR03Green((pixel)); \
388         LBR03Blue((pixel)); \
389         }
390 
391 /* LBR04: Replicate top 4 bits */
392 
393 #define LBR04PacketRed(pixelpacket) \
394    { \
395      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
396      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397    }
398 #define LBR04PacketGreen(pixelpacket) \
399    { \
400      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
401      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402    }
403 #define LBR04PacketBlue(pixelpacket) \
404    { \
405      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
406      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407    }
408 #define LBR04PacketAlpha(pixelpacket) \
409    { \
410      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
411      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
412    }
413 
414 #define LBR04PacketRGB(pixelpacket) \
415         { \
416         LBR04PacketRed((pixelpacket)); \
417         LBR04PacketGreen((pixelpacket)); \
418         LBR04PacketBlue((pixelpacket)); \
419         }
420 
421 #define LBR04PacketRGBA(pixelpacket) \
422         { \
423         LBR04PacketRGB((pixelpacket)); \
424         LBR04PacketAlpha((pixelpacket)); \
425         }
426 
427 #define LBR04PixelRed(pixel) \
428    { \
429      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
430        & 0xf0; \
431      SetPixelRed(image,\
432        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
433    }
434 #define LBR04PixelGreen(pixel) \
435    { \
436      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
437        & 0xf0; \
438      SetPixelGreen(image,\
439        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
440    }
441 #define LBR04PixelBlue(pixel) \
442    { \
443      unsigned char lbr_bits= \
444        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
445      SetPixelBlue(image,\
446        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
447    }
448 #define LBR04PixelAlpha(pixel) \
449    { \
450      unsigned char lbr_bits= \
451        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
452      SetPixelAlpha(image,\
453        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
454    }
455 
456 #define LBR04PixelRGB(pixel) \
457         { \
458         LBR04PixelRed((pixel)); \
459         LBR04PixelGreen((pixel)); \
460         LBR04PixelBlue((pixel)); \
461         }
462 
463 #define LBR04PixelRGBA(pixel) \
464         { \
465         LBR04PixelRGB((pixel)); \
466         LBR04PixelAlpha((pixel)); \
467         }
468 
469 /*
470   Establish thread safety.
471   setjmp/longjmp is claimed to be safe on these platforms:
472   setjmp/longjmp is alleged to be unsafe on these platforms:
473 */
474 #ifdef PNG_SETJMP_SUPPORTED
475 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
476 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
477 # endif
478 
479 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
480 static SemaphoreInfo
481   *ping_semaphore = (SemaphoreInfo *) NULL;
482 # endif
483 #endif
484 
485 /*
486   This temporary until I set up malloc'ed object attributes array.
487   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
488   waste more memory.
489 */
490 #define MNG_MAX_OBJECTS 256
491 
492 /*
493   If this not defined, spec is interpreted strictly.  If it is
494   defined, an attempt will be made to recover from some errors,
495   including
496       o global PLTE too short
497 */
498 #undef MNG_LOOSE
499 
500 /*
501   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
502   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
503   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
504   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
505   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
506   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
507   will be enabled by default in libpng-1.2.0.
508 */
509 #ifdef PNG_MNG_FEATURES_SUPPORTED
510 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
511 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
512 #  endif
513 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
514 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
515 #  endif
516 #endif
517 
518 /*
519   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
520   This macro is only defined in libpng-1.0.3 and later.
521   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
522 */
523 #ifndef PNG_UINT_31_MAX
524 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
525 #endif
526 
527 /*
528   Constant strings for known chunk types.  If you need to add a chunk,
529   add a string holding the name here.   To make the code more
530   portable, we use ASCII numbers like this, not characters.
531 */
532 
533 static const png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
534 static const png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
535 static const png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
536 static const png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
537 static const png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
538 static const png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
539 static const png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
540 static const png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
541 static const png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
542 static const png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
543 static const png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
544 static const png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
545 static const png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
546 static const png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
547 static const png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
548 static const png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
549 static const png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
550 static const png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
551 static const png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
552 static const png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
553 static const png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
554 static const png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
555 static const png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
556 static const png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
557 static const png_byte mng_caNv[5]={ 99,  97,  78, 118, (png_byte) '\0'};
558 static const png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
559 static const png_byte mng_eXIf[5]={101,  88,  73, 102, (png_byte) '\0'};
560 static const png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
561 static const png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
562 static const png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
563 static const png_byte mng_orNT[5]={111, 114,  78,  84, (png_byte) '\0'};
564 static const png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
565 static const png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
566 static const png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
567 static const png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
568 static const png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
569 static const png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
570 
571 #if defined(JNG_SUPPORTED)
572 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
573 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
574 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
575 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
576 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
577 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
578 #endif
579 
580 #if 0
581 /* Other known chunks that are not yet supported by ImageMagick: */
582 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
583 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
584 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
585 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
586 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
587 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
588 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
589 #endif
590 
591 typedef struct _MngBox
592 {
593   long
594     left,
595     right,
596     top,
597     bottom;
598 } MngBox;
599 
600 typedef struct _MngPair
601 {
602   volatile long
603     a,
604     b;
605 } MngPair;
606 
607 #ifdef MNG_OBJECT_BUFFERS
608 typedef struct _MngBuffer
609 {
610 
611   size_t
612     height,
613     width;
614 
615   Image
616     *image;
617 
618   png_color
619     plte[256];
620 
621   int
622     reference_count;
623 
624   unsigned char
625     alpha_sample_depth,
626     compression_method,
627     color_type,
628     concrete,
629     filter_method,
630     frozen,
631     image_type,
632     interlace_method,
633     pixel_sample_depth,
634     plte_length,
635     sample_depth,
636     viewable;
637 } MngBuffer;
638 #endif
639 
640 typedef struct _MngInfo
641 {
642 
643 #ifdef MNG_OBJECT_BUFFERS
644   MngBuffer
645     *ob[MNG_MAX_OBJECTS];
646 #endif
647 
648   Image *
649     image;
650 
651   RectangleInfo
652     page;
653 
654   int
655     adjoin,
656 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
657     bytes_in_read_buffer,
658     found_empty_plte,
659 #endif
660     equal_backgrounds,
661     equal_chrms,
662     equal_gammas,
663 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
664     defined(PNG_MNG_FEATURES_SUPPORTED)
665     equal_palettes,
666 #endif
667     equal_physs,
668     equal_srgbs,
669     framing_mode,
670     have_global_bkgd,
671     have_global_chrm,
672     have_global_gama,
673     have_global_phys,
674     have_global_sbit,
675     have_global_srgb,
676     have_saved_bkgd_index,
677     have_write_global_chrm,
678     have_write_global_gama,
679     have_write_global_plte,
680     have_write_global_srgb,
681     need_fram,
682     object_id,
683     old_framing_mode,
684     saved_bkgd_index;
685 
686   int
687     new_number_colors;
688 
689   ssize_t
690     image_found,
691     loop_count[256],
692     loop_iteration[256],
693     scenes_found,
694     x_off[MNG_MAX_OBJECTS],
695     y_off[MNG_MAX_OBJECTS];
696 
697   MngBox
698     clip,
699     frame,
700     image_box,
701     object_clip[MNG_MAX_OBJECTS];
702 
703   unsigned char
704     /* These flags could be combined into one byte */
705     exists[MNG_MAX_OBJECTS],
706     frozen[MNG_MAX_OBJECTS],
707     loop_active[256],
708     invisible[MNG_MAX_OBJECTS],
709     viewable[MNG_MAX_OBJECTS];
710 
711   MagickOffsetType
712     loop_jump[256];
713 
714   png_colorp
715     global_plte;
716 
717   png_color_8
718     global_sbit;
719 
720   png_byte
721 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
722     read_buffer[8],
723 #endif
724     global_trns[256];
725 
726   float
727     global_gamma;
728 
729   ChromaticityInfo
730     global_chrm;
731 
732   RenderingIntent
733     global_srgb_intent;
734 
735   unsigned long
736     delay,
737     global_plte_length,
738     global_trns_length,
739     global_x_pixels_per_unit,
740     global_y_pixels_per_unit,
741     mng_width,
742     mng_height,
743     ticks_per_second;
744 
745   MagickBooleanType
746     need_blob;
747 
748   unsigned int
749     IsPalette,
750     global_phys_unit_type,
751     basi_warning,
752     clon_warning,
753     dhdr_warning,
754     jhdr_warning,
755     magn_warning,
756     past_warning,
757     phyg_warning,
758     phys_warning,
759     sbit_warning,
760     show_warning,
761     mng_type,
762     write_mng,
763     write_png_colortype,
764     write_png_depth,
765     write_png_compression_level,
766     write_png_compression_strategy,
767     write_png_compression_filter,
768     write_png8,
769     write_png24,
770     write_png32,
771     write_png48,
772     write_png64;
773 
774 #ifdef MNG_BASI_SUPPORTED
775   unsigned long
776     basi_width,
777     basi_height;
778 
779   unsigned int
780     basi_depth,
781     basi_color_type,
782     basi_compression_method,
783     basi_filter_type,
784     basi_interlace_method,
785     basi_red,
786     basi_green,
787     basi_blue,
788     basi_alpha,
789     basi_viewable;
790 #endif
791 
792   png_uint_16
793     magn_first,
794     magn_last,
795     magn_mb,
796     magn_ml,
797     magn_mr,
798     magn_mt,
799     magn_mx,
800     magn_my,
801     magn_methx,
802     magn_methy;
803 
804   PixelInfo
805     mng_global_bkgd;
806 
807   /* Added at version 6.6.6-7 */
808   MagickBooleanType
809     ping_exclude_bKGD,
810     ping_exclude_cHRM,
811     ping_exclude_date,
812     ping_exclude_eXIf,
813     ping_exclude_EXIF,
814     ping_exclude_gAMA,
815     ping_exclude_iCCP,
816     /* ping_exclude_iTXt, */
817     ping_exclude_oFFs,
818     ping_exclude_pHYs,
819     ping_exclude_sRGB,
820     ping_exclude_tEXt,
821     ping_exclude_tRNS,
822     ping_exclude_caNv,
823     ping_exclude_zCCP, /* hex-encoded iCCP */
824     ping_exclude_zTXt,
825     ping_preserve_colormap,
826   /* Added at version 6.8.5-7 */
827     ping_preserve_iCCP,
828   /* Added at version 6.8.9-9 */
829     ping_exclude_tIME;
830 
831 } MngInfo;
832 #endif /* VER */
833 
834 /*
835   Forward declarations.
836 */
837 static MagickBooleanType
838   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
839 
840 static MagickBooleanType
841   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
842 
843 #if defined(JNG_SUPPORTED)
844 static MagickBooleanType
845   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
846 #endif
847 
848 #if PNG_LIBPNG_VER > 10011
849 
850 
851 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
852 static MagickBooleanType
LosslessReduceDepthOK(Image * image,ExceptionInfo * exception)853 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
854 {
855     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
856      *
857      * This is true if the high byte and the next highest byte of
858      * each sample of the image, the colormap, and the background color
859      * are equal to each other.  We check this by seeing if the samples
860      * are unchanged when we scale them down to 8 and back up to Quantum.
861      *
862      * We don't use the method GetImageDepth() because it doesn't check
863      * background and doesn't handle PseudoClass specially.
864      */
865 
866 #define QuantumToCharToQuantumEqQuantum(quantum) \
867  ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
868 
869     MagickBooleanType
870       ok_to_reduce=MagickFalse;
871 
872     if (image->depth >= 16)
873       {
874 
875         const Quantum
876           *p;
877 
878         ok_to_reduce=
879            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
880            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
881            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
882            MagickTrue : MagickFalse;
883 
884         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
885           {
886             int indx;
887 
888             for (indx=0; indx < (ssize_t) image->colors; indx++)
889               {
890                 ok_to_reduce=(
891                    QuantumToCharToQuantumEqQuantum(
892                    image->colormap[indx].red) &&
893                    QuantumToCharToQuantumEqQuantum(
894                    image->colormap[indx].green) &&
895                    QuantumToCharToQuantumEqQuantum(
896                    image->colormap[indx].blue)) ?
897                    MagickTrue : MagickFalse;
898 
899                 if (ok_to_reduce == MagickFalse)
900                    break;
901               }
902           }
903 
904         if ((ok_to_reduce != MagickFalse) &&
905             (image->storage_class != PseudoClass))
906           {
907             ssize_t
908               y;
909 
910             register ssize_t
911               x;
912 
913             for (y=0; y < (ssize_t) image->rows; y++)
914             {
915               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
916 
917               if (p == (const Quantum *) NULL)
918                 {
919                   ok_to_reduce = MagickFalse;
920                   break;
921                 }
922 
923               for (x=(ssize_t) image->columns-1; x >= 0; x--)
924               {
925                 ok_to_reduce=
926                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
927                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
928                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
929                    MagickTrue : MagickFalse;
930 
931                 if (ok_to_reduce == MagickFalse)
932                   break;
933 
934                 p+=GetPixelChannels(image);
935               }
936               if (x >= 0)
937                 break;
938             }
939           }
940 
941         if (ok_to_reduce != MagickFalse)
942           {
943             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
944                 "    OK to reduce PNG bit depth to 8 without loss of info");
945           }
946         else
947           {
948             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
949                 "    Not OK to reduce PNG bit depth to 8 without losing info");
950           }
951       }
952 
953     return ok_to_reduce;
954 }
955 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
956 
PngColorTypeToString(const unsigned int color_type)957 static const char* PngColorTypeToString(const unsigned int color_type)
958 {
959   const char
960     *result = "Unknown";
961 
962   switch (color_type)
963     {
964     case PNG_COLOR_TYPE_GRAY:
965       result = "Gray";
966       break;
967     case PNG_COLOR_TYPE_GRAY_ALPHA:
968       result = "Gray+Alpha";
969       break;
970     case PNG_COLOR_TYPE_PALETTE:
971       result = "Palette";
972       break;
973     case PNG_COLOR_TYPE_RGB:
974       result = "RGB";
975       break;
976     case PNG_COLOR_TYPE_RGB_ALPHA:
977       result = "RGB+Alpha";
978       break;
979     }
980 
981   return result;
982 }
983 
984 static int
Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)985 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
986 {
987   switch (orientation)
988   {
989     /* Convert to Exif orientations as defined in "Exchangeable image file
990      * format for digital still cameras: Exif Version 2.31",
991      * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
992      */
993 
994     case TopLeftOrientation:
995        return 1;
996     case TopRightOrientation:
997        return 2;
998     case BottomRightOrientation:
999        return 3;
1000     case BottomLeftOrientation:
1001        return 4;
1002     case LeftTopOrientation:
1003        return 5;
1004     case RightTopOrientation:
1005        return 6;
1006     case RightBottomOrientation:
1007        return 7;
1008     case LeftBottomOrientation:
1009        return 8;
1010     case UndefinedOrientation:
1011     default:
1012        return 0;
1013   }
1014 }
1015 static OrientationType
Magick_Orientation_from_Exif_Orientation(const int orientation)1016 Magick_Orientation_from_Exif_Orientation(const int orientation)
1017 {
1018   switch (orientation)
1019   {
1020     case 1:
1021       return TopLeftOrientation;
1022     case 2:
1023       return TopRightOrientation;
1024     case 3:
1025       return BottomRightOrientation;
1026     case 4:
1027       return BottomLeftOrientation;
1028     case 5:
1029       return LeftTopOrientation;
1030     case 6:
1031       return RightTopOrientation;
1032     case 7:
1033       return RightBottomOrientation;
1034     case 8:
1035       return LeftBottomOrientation;
1036     case 0:
1037     default:
1038       return UndefinedOrientation;
1039   }
1040 }
1041 
1042 static int
Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)1043 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1044 {
1045   switch (intent)
1046   {
1047     case PerceptualIntent:
1048        return 0;
1049 
1050     case RelativeIntent:
1051        return 1;
1052 
1053     case SaturationIntent:
1054        return 2;
1055 
1056     case AbsoluteIntent:
1057        return 3;
1058 
1059     default:
1060        return -1;
1061   }
1062 }
1063 
1064 static RenderingIntent
Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)1065 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1066 {
1067   switch (ping_intent)
1068   {
1069     case 0:
1070       return PerceptualIntent;
1071 
1072     case 1:
1073       return RelativeIntent;
1074 
1075     case 2:
1076       return SaturationIntent;
1077 
1078     case 3:
1079       return AbsoluteIntent;
1080 
1081     default:
1082       return UndefinedIntent;
1083     }
1084 }
1085 
1086 static const char *
Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)1087 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1088 {
1089   switch (ping_intent)
1090   {
1091     case 0:
1092       return "Perceptual Intent";
1093 
1094     case 1:
1095       return "Relative Intent";
1096 
1097     case 2:
1098       return "Saturation Intent";
1099 
1100     case 3:
1101       return "Absolute Intent";
1102 
1103     default:
1104       return "Undefined Intent";
1105     }
1106 }
1107 
1108 static const char *
Magick_ColorType_from_PNG_ColorType(const int ping_colortype)1109 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1110 {
1111   switch (ping_colortype)
1112   {
1113     case 0:
1114       return "Grayscale";
1115 
1116     case 2:
1117       return "Truecolor";
1118 
1119     case 3:
1120       return "Indexed";
1121 
1122     case 4:
1123       return "GrayAlpha";
1124 
1125     case 6:
1126       return "RGBA";
1127 
1128     default:
1129       return "UndefinedColorType";
1130     }
1131 }
1132 
1133 #endif /* PNG_LIBPNG_VER > 10011 */
1134 #endif /* MAGICKCORE_PNG_DELEGATE */
1135 
1136 /*
1137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1138 %                                                                             %
1139 %                                                                             %
1140 %                                                                             %
1141 %   I s M N G                                                                 %
1142 %                                                                             %
1143 %                                                                             %
1144 %                                                                             %
1145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1146 %
1147 %  IsMNG() returns MagickTrue if the image format type, identified by the
1148 %  magick string, is MNG.
1149 %
1150 %  The format of the IsMNG method is:
1151 %
1152 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1153 %
1154 %  A description of each parameter follows:
1155 %
1156 %    o magick: compare image format pattern against these bytes.
1157 %
1158 %    o length: Specifies the length of the magick string.
1159 %
1160 %
1161 */
IsMNG(const unsigned char * magick,const size_t length)1162 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1163 {
1164   if (length < 8)
1165     return(MagickFalse);
1166 
1167   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1168     return(MagickTrue);
1169 
1170   return(MagickFalse);
1171 }
1172 
1173 /*
1174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175 %                                                                             %
1176 %                                                                             %
1177 %                                                                             %
1178 %   I s J N G                                                                 %
1179 %                                                                             %
1180 %                                                                             %
1181 %                                                                             %
1182 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1183 %
1184 %  IsJNG() returns MagickTrue if the image format type, identified by the
1185 %  magick string, is JNG.
1186 %
1187 %  The format of the IsJNG method is:
1188 %
1189 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1190 %
1191 %  A description of each parameter follows:
1192 %
1193 %    o magick: compare image format pattern against these bytes.
1194 %
1195 %    o length: Specifies the length of the magick string.
1196 %
1197 %
1198 */
IsJNG(const unsigned char * magick,const size_t length)1199 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1200 {
1201   if (length < 8)
1202     return(MagickFalse);
1203 
1204   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1205     return(MagickTrue);
1206 
1207   return(MagickFalse);
1208 }
1209 
1210 /*
1211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1212 %                                                                             %
1213 %                                                                             %
1214 %                                                                             %
1215 %   I s P N G                                                                 %
1216 %                                                                             %
1217 %                                                                             %
1218 %                                                                             %
1219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1220 %
1221 %  IsPNG() returns MagickTrue if the image format type, identified by the
1222 %  magick string, is PNG.
1223 %
1224 %  The format of the IsPNG method is:
1225 %
1226 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1227 %
1228 %  A description of each parameter follows:
1229 %
1230 %    o magick: compare image format pattern against these bytes.
1231 %
1232 %    o length: Specifies the length of the magick string.
1233 %
1234 */
IsPNG(const unsigned char * magick,const size_t length)1235 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1236 {
1237   if (length < 8)
1238     return(MagickFalse);
1239 
1240   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1241     return(MagickTrue);
1242 
1243   return(MagickFalse);
1244 }
1245 
1246 #if defined(MAGICKCORE_PNG_DELEGATE)
1247 #if defined(__cplusplus) || defined(c_plusplus)
1248 extern "C" {
1249 #endif
1250 
1251 #if (PNG_LIBPNG_VER > 10011)
WriteBlobMSBULong(Image * image,const size_t value)1252 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1253 {
1254   unsigned char
1255     buffer[4];
1256 
1257   assert(image != (Image *) NULL);
1258   assert(image->signature == MagickCoreSignature);
1259   buffer[0]=(unsigned char) (value >> 24);
1260   buffer[1]=(unsigned char) (value >> 16);
1261   buffer[2]=(unsigned char) (value >> 8);
1262   buffer[3]=(unsigned char) value;
1263   return((size_t) WriteBlob(image,4,buffer));
1264 }
1265 
PNGLong(png_bytep p,png_uint_32 value)1266 static void PNGLong(png_bytep p,png_uint_32 value)
1267 {
1268   *p++=(png_byte) ((value >> 24) & 0xff);
1269   *p++=(png_byte) ((value >> 16) & 0xff);
1270   *p++=(png_byte) ((value >> 8) & 0xff);
1271   *p++=(png_byte) (value & 0xff);
1272 }
1273 
PNGsLong(png_bytep p,png_int_32 value)1274 static void PNGsLong(png_bytep p,png_int_32 value)
1275 {
1276   *p++=(png_byte) ((value >> 24) & 0xff);
1277   *p++=(png_byte) ((value >> 16) & 0xff);
1278   *p++=(png_byte) ((value >> 8) & 0xff);
1279   *p++=(png_byte) (value & 0xff);
1280 }
1281 
PNGShort(png_bytep p,png_uint_16 value)1282 static void PNGShort(png_bytep p,png_uint_16 value)
1283 {
1284   *p++=(png_byte) ((value >> 8) & 0xff);
1285   *p++=(png_byte) (value & 0xff);
1286 }
1287 
PNGType(png_bytep p,const png_byte * type)1288 static void PNGType(png_bytep p,const png_byte *type)
1289 {
1290   (void) memcpy(p,type,4*sizeof(png_byte));
1291 }
1292 
LogPNGChunk(MagickBooleanType logging,const png_byte * type,size_t length)1293 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1294    size_t length)
1295 {
1296   if (logging != MagickFalse)
1297     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1298       "  Writing %c%c%c%c chunk, length: %.20g",
1299       type[0],type[1],type[2],type[3],(double) length);
1300 }
1301 #endif /* PNG_LIBPNG_VER > 10011 */
1302 
1303 #if defined(__cplusplus) || defined(c_plusplus)
1304 }
1305 #endif
1306 
1307 #if PNG_LIBPNG_VER > 10011
1308 /*
1309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1310 %                                                                             %
1311 %                                                                             %
1312 %                                                                             %
1313 %   R e a d P N G I m a g e                                                   %
1314 %                                                                             %
1315 %                                                                             %
1316 %                                                                             %
1317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1318 %
1319 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1320 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1321 %  allocates the memory necessary for the new Image structure and returns a
1322 %  pointer to the new image or set of images.
1323 %
1324 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1325 %
1326 %  The format of the ReadPNGImage method is:
1327 %
1328 %     Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1329 %
1330 %  A description of each parameter follows:
1331 %
1332 %    o image_info: the image info.
1333 %
1334 %    o exception: return any errors or warnings in this structure.
1335 %
1336 %  To do, more or less in chronological order (as of version 5.5.2,
1337 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1338 %
1339 %    Get 16-bit cheap transparency working.
1340 %
1341 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1342 %
1343 %    Preserve all unknown and not-yet-handled known chunks found in input
1344 %    PNG file and copy them into output PNG files according to the PNG
1345 %    copying rules.
1346 %
1347 %    (At this point, PNG encoding should be in full MNG compliance)
1348 %
1349 %    Provide options for choice of background to use when the MNG BACK
1350 %    chunk is not present or is not mandatory (i.e., leave transparent,
1351 %    user specified, MNG BACK, PNG bKGD)
1352 %
1353 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1354 %    efficiently by linking in the duplicate frames.].
1355 %
1356 %    Decode and act on the MHDR simplicity profile (offer option to reject
1357 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1358 %
1359 %    Upgrade to full MNG without Delta-PNG.
1360 %
1361 %        o  BACK [done a while ago except for background image ID]
1362 %        o  MOVE [done 15 May 1999]
1363 %        o  CLIP [done 15 May 1999]
1364 %        o  DISC [done 19 May 1999]
1365 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1366 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1367 %        o  SHOW
1368 %        o  PAST
1369 %        o  BASI
1370 %        o  MNG-level tEXt/iTXt/zTXt
1371 %        o  pHYg
1372 %        o  pHYs
1373 %        o  sBIT
1374 %        o  bKGD
1375 %        o  iTXt (wait for libpng implementation).
1376 %
1377 %    Use the scene signature to discover when an identical scene is
1378 %    being reused, and just point to the original image->exception instead
1379 %    of storing another set of pixels.  This not specific to MNG
1380 %    but could be applied generally.
1381 %
1382 %    Upgrade to full MNG with Delta-PNG.
1383 %
1384 %    JNG tEXt/iTXt/zTXt
1385 %
1386 %    We will not attempt to read files containing the CgBI chunk.
1387 %    They are really Xcode files meant for display on the iPhone.
1388 %    These are not valid PNG files and it is impossible to recover
1389 %    the original PNG from files that have been converted to Xcode-PNG,
1390 %    since irretrievable loss of color data has occurred due to the
1391 %    use of premultiplied alpha.
1392 */
1393 
1394 #if defined(__cplusplus) || defined(c_plusplus)
1395 extern "C" {
1396 #endif
1397 
1398 /*
1399   This the function that does the actual reading of data.  It is
1400   the same as the one supplied in libpng, except that it receives the
1401   datastream from the ReadBlob() function instead of standard input.
1402 */
png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1403 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1404 {
1405   Image
1406     *image;
1407 
1408   image=(Image *) png_get_io_ptr(png_ptr);
1409   if (length != 0)
1410     {
1411       png_size_t
1412         check;
1413 
1414       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1415       if (check != length)
1416         {
1417           char
1418             msg[MagickPathExtent];
1419 
1420           (void) FormatLocaleString(msg,MagickPathExtent,
1421             "Expected %.20g bytes; found %.20g bytes",(double) length,
1422             (double) check);
1423           png_warning(png_ptr,msg);
1424           png_error(png_ptr,"Read Exception");
1425         }
1426     }
1427 }
1428 
1429 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1430     !defined(PNG_MNG_FEATURES_SUPPORTED)
1431 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1432  * older than libpng-1.0.3a, which was the first to allow the empty
1433  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1434  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1435  * encountered after an empty PLTE, so we have to look ahead for bKGD
1436  * chunks and remove them from the datastream that is passed to libpng,
1437  * and store their contents for later use.
1438  */
mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1439 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1440 {
1441   MngInfo
1442     *mng_info;
1443 
1444   Image
1445     *image;
1446 
1447   png_size_t
1448     check;
1449 
1450   register ssize_t
1451     i;
1452 
1453   i=0;
1454   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1455   image=(Image *) mng_info->image;
1456   while (mng_info->bytes_in_read_buffer && length)
1457   {
1458     data[i]=mng_info->read_buffer[i];
1459     mng_info->bytes_in_read_buffer--;
1460     length--;
1461     i++;
1462   }
1463   if (length != 0)
1464     {
1465       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1466 
1467       if (check != length)
1468         png_error(png_ptr,"Read Exception");
1469 
1470       if (length == 4)
1471         {
1472           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1473               (data[3] == 0))
1474             {
1475               check=(png_size_t) ReadBlob(image,(size_t) length,
1476                 (char *) mng_info->read_buffer);
1477               mng_info->read_buffer[4]=0;
1478               mng_info->bytes_in_read_buffer=4;
1479               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1480                 mng_info->found_empty_plte=MagickTrue;
1481               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1482                 {
1483                   mng_info->found_empty_plte=MagickFalse;
1484                   mng_info->have_saved_bkgd_index=MagickFalse;
1485                 }
1486             }
1487 
1488           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1489               (data[3] == 1))
1490             {
1491               check=(png_size_t) ReadBlob(image,(size_t) length,
1492                 (char *) mng_info->read_buffer);
1493               mng_info->read_buffer[4]=0;
1494               mng_info->bytes_in_read_buffer=4;
1495               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1496                 if (mng_info->found_empty_plte)
1497                   {
1498                     /*
1499                       Skip the bKGD data byte and CRC.
1500                     */
1501                     check=(png_size_t)
1502                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1503                     check=(png_size_t) ReadBlob(image,(size_t) length,
1504                       (char *) mng_info->read_buffer);
1505                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1506                     mng_info->have_saved_bkgd_index=MagickTrue;
1507                     mng_info->bytes_in_read_buffer=0;
1508                   }
1509             }
1510         }
1511     }
1512 }
1513 #endif
1514 
png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)1515 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1516 {
1517   Image
1518     *image;
1519 
1520   image=(Image *) png_get_io_ptr(png_ptr);
1521   if (length != 0)
1522     {
1523       png_size_t
1524         check;
1525 
1526       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1527 
1528       if (check != length)
1529         png_error(png_ptr,"WriteBlob Failed");
1530     }
1531 }
1532 
png_flush_data(png_structp png_ptr)1533 static void png_flush_data(png_structp png_ptr)
1534 {
1535   (void) png_ptr;
1536 }
1537 
1538 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
PalettesAreEqual(Image * a,Image * b)1539 static int PalettesAreEqual(Image *a,Image *b)
1540 {
1541   ssize_t
1542     i;
1543 
1544   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1545     return((int) MagickFalse);
1546 
1547   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1548     return((int) MagickFalse);
1549 
1550   if (a->colors != b->colors)
1551     return((int) MagickFalse);
1552 
1553   for (i=0; i < (ssize_t) a->colors; i++)
1554   {
1555     if ((a->colormap[i].red != b->colormap[i].red) ||
1556         (a->colormap[i].green != b->colormap[i].green) ||
1557         (a->colormap[i].blue != b->colormap[i].blue))
1558       return((int) MagickFalse);
1559   }
1560 
1561   return((int) MagickTrue);
1562 }
1563 #endif
1564 
MngInfoDiscardObject(MngInfo * mng_info,int i)1565 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1566 {
1567   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1568       mng_info->exists[i] && !mng_info->frozen[i])
1569     {
1570 #ifdef MNG_OBJECT_BUFFERS
1571       if (mng_info->ob[i] != (MngBuffer *) NULL)
1572         {
1573           if (mng_info->ob[i]->reference_count > 0)
1574             mng_info->ob[i]->reference_count--;
1575 
1576           if (mng_info->ob[i]->reference_count == 0)
1577             {
1578               if (mng_info->ob[i]->image != (Image *) NULL)
1579                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1580 
1581               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1582             }
1583         }
1584       mng_info->ob[i]=(MngBuffer *) NULL;
1585 #endif
1586       mng_info->exists[i]=MagickFalse;
1587       mng_info->invisible[i]=MagickFalse;
1588       mng_info->viewable[i]=MagickFalse;
1589       mng_info->frozen[i]=MagickFalse;
1590       mng_info->x_off[i]=0;
1591       mng_info->y_off[i]=0;
1592       mng_info->object_clip[i].left=0;
1593       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1594       mng_info->object_clip[i].top=0;
1595       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1596     }
1597 }
1598 
MngInfoFreeStruct(MngInfo * mng_info)1599 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1600 {
1601   register ssize_t
1602     i;
1603 
1604   if (mng_info == (MngInfo *) NULL)
1605     return((MngInfo *) NULL);
1606 
1607   for (i=1; i < MNG_MAX_OBJECTS; i++)
1608     MngInfoDiscardObject(mng_info,i);
1609 
1610   mng_info->global_plte=(png_colorp)
1611     RelinquishMagickMemory(mng_info->global_plte);
1612 
1613   return((MngInfo *) RelinquishMagickMemory(mng_info));
1614 }
1615 
mng_minimum_box(MngBox box1,MngBox box2)1616 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1617 {
1618   MngBox
1619     box;
1620 
1621   box=box1;
1622   if (box.left < box2.left)
1623     box.left=box2.left;
1624 
1625   if (box.top < box2.top)
1626     box.top=box2.top;
1627 
1628   if (box.right > box2.right)
1629     box.right=box2.right;
1630 
1631   if (box.bottom > box2.bottom)
1632     box.bottom=box2.bottom;
1633 
1634   return box;
1635 }
1636 
mng_get_long(unsigned char * p)1637 static long mng_get_long(unsigned char *p)
1638 {
1639   return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1640     ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1641 }
1642 
mng_read_box(MngBox previous_box,char delta_type,unsigned char * p)1643 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1644   unsigned char *p)
1645 {
1646    MngBox
1647       box;
1648 
1649   /*
1650     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1651   */
1652   box.left=mng_get_long(p);
1653   box.right=mng_get_long(&p[4]);
1654   box.top=mng_get_long(&p[8]);
1655   box.bottom=mng_get_long(&p[12]);
1656   if (delta_type != 0)
1657     {
1658       box.left+=previous_box.left;
1659       box.right+=previous_box.right;
1660       box.top+=previous_box.top;
1661       box.bottom+=previous_box.bottom;
1662     }
1663 
1664   return(box);
1665 }
1666 
mng_read_pair(MngPair previous_pair,int delta_type,unsigned char * p)1667 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1668   unsigned char *p)
1669 {
1670   MngPair
1671     pair;
1672 
1673   /*
1674     Read two ssize_t's from CLON, MOVE or PAST chunk
1675   */
1676   pair.a=mng_get_long(p);
1677   pair.b=mng_get_long(&p[4]);
1678   if (delta_type != 0)
1679     {
1680       pair.a+=previous_pair.a;
1681       pair.b+=previous_pair.b;
1682     }
1683 
1684   return(pair);
1685 }
1686 
1687 typedef struct _PNGErrorInfo
1688 {
1689   Image
1690     *image;
1691 
1692   ExceptionInfo
1693     *exception;
1694 } PNGErrorInfo;
1695 
MagickPNGErrorHandler(png_struct * ping,png_const_charp message)1696 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1697 {
1698   ExceptionInfo
1699     *exception;
1700 
1701   Image
1702     *image;
1703 
1704   PNGErrorInfo
1705     *error_info;
1706 
1707   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1708   image=error_info->image;
1709   exception=error_info->exception;
1710 
1711   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1712     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1713 
1714   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1715     "`%s'",image->filename);
1716 
1717 #if (PNG_LIBPNG_VER < 10500)
1718   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1719    * are building with libpng-1.4.x and can be ignored.
1720    */
1721   longjmp(ping->jmpbuf,1);
1722 #else
1723   png_longjmp(ping,1);
1724 #endif
1725 }
1726 
MagickPNGWarningHandler(png_struct * ping,png_const_charp message)1727 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1728 {
1729   ExceptionInfo
1730     *exception;
1731 
1732   Image
1733     *image;
1734 
1735   PNGErrorInfo
1736     *error_info;
1737 
1738   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1739     png_error(ping, message);
1740 
1741   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1742   image=error_info->image;
1743   exception=error_info->exception;
1744   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1745     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1746 
1747   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1748     message,"`%s'",image->filename);
1749 }
1750 
1751 #ifdef PNG_USER_MEM_SUPPORTED
1752 #if PNG_LIBPNG_VER >= 10400
Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)1753 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1754 #else
1755 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1756 #endif
1757 {
1758   (void) png_ptr;
1759   return((png_voidp) AcquireMagickMemory((size_t) size));
1760 }
1761 
1762 /*
1763   Free a pointer.  It is removed from the list at the same time.
1764 */
Magick_png_free(png_structp png_ptr,png_voidp ptr)1765 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1766 {
1767   (void) png_ptr;
1768   ptr=RelinquishMagickMemory(ptr);
1769   return((png_free_ptr) NULL);
1770 }
1771 #endif
1772 
1773 #if defined(__cplusplus) || defined(c_plusplus)
1774 }
1775 #endif
1776 
1777 static int
Magick_png_read_raw_profile(png_struct * ping,Image * image,const ImageInfo * image_info,png_textp text,int ii,ExceptionInfo * exception)1778 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1779    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1780 {
1781   register ssize_t
1782     i;
1783 
1784   register unsigned char
1785     *dp;
1786 
1787   register png_charp
1788     sp;
1789 
1790   size_t
1791     extent,
1792     length,
1793     nibbles;
1794 
1795   StringInfo
1796     *profile;
1797 
1798   static const unsigned char
1799     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1800                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1801                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1802                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1803                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1804                  13,14,15};
1805 
1806   sp=text[ii].text+1;
1807   extent=text[ii].text_length;
1808   /* look for newline */
1809   while ((*sp != '\n') && extent--)
1810     sp++;
1811 
1812   /* look for length */
1813   while (((*sp == '\0' || *sp == ' ' || *sp == '\n')) && extent--)
1814      sp++;
1815 
1816   if (extent == 0)
1817     {
1818       png_warning(ping,"invalid profile length");
1819       return(MagickFalse);
1820     }
1821 
1822   length=StringToLong(sp);
1823 
1824   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1825        "      length: %lu",(unsigned long) length);
1826 
1827   while ((*sp != ' ' && *sp != '\n') && extent--)
1828     sp++;
1829 
1830   if (extent == 0)
1831     {
1832       png_warning(ping,"missing profile length");
1833       return(MagickFalse);
1834     }
1835 
1836   /* allocate space */
1837   if (length == 0)
1838   {
1839     png_warning(ping,"invalid profile length");
1840     return(MagickFalse);
1841   }
1842 
1843   profile=BlobToStringInfo((const void *) NULL,length);
1844 
1845   if (profile == (StringInfo *) NULL)
1846   {
1847     png_warning(ping, "unable to copy profile");
1848     return(MagickFalse);
1849   }
1850 
1851   /* copy profile, skipping white space and column 1 "=" signs */
1852   dp=GetStringInfoDatum(profile);
1853   nibbles=length*2;
1854 
1855   for (i=0; i < (ssize_t) nibbles; i++)
1856   {
1857     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1858     {
1859       if (*sp == '\0')
1860         {
1861           png_warning(ping, "ran out of profile data");
1862           profile=DestroyStringInfo(profile);
1863           return(MagickFalse);
1864         }
1865       sp++;
1866     }
1867 
1868     if (i%2 == 0)
1869       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1870 
1871     else
1872       (*dp++)+=unhex[(int) *sp++];
1873   }
1874   /*
1875     We have already read "Raw profile type.
1876   */
1877   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1878   profile=DestroyStringInfo(profile);
1879 
1880   if (image_info->verbose)
1881     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1882 
1883   return MagickTrue;
1884 }
1885 
PNGSetExifProfile(Image * image,png_size_t size,png_byte * data,ExceptionInfo * exception)1886 static int PNGSetExifProfile(Image *image,png_size_t size,png_byte *data,
1887   ExceptionInfo *exception)
1888 {
1889   StringInfo
1890     *profile;
1891 
1892   unsigned char
1893     *p;
1894 
1895   png_byte
1896     *s;
1897 
1898   size_t
1899     i;
1900 
1901   profile=BlobToStringInfo((const void *) NULL,size+6);
1902 
1903   if (profile == (StringInfo *) NULL)
1904     {
1905       (void) ThrowMagickException(exception,GetMagickModule(),
1906         ResourceLimitError,"MemoryAllocationFailed","`%s'",
1907         image->filename);
1908       return(-1);
1909     }
1910   p=GetStringInfoDatum(profile);
1911 
1912   /* Initialize profile with "Exif\0\0" */
1913   *p++ ='E';
1914   *p++ ='x';
1915   *p++ ='i';
1916   *p++ ='f';
1917   *p++ ='\0';
1918   *p++ ='\0';
1919 
1920   s=data;
1921   i=0;
1922   if (size > 6)
1923     {
1924       /* Skip first 6 bytes if "Exif\0\0" is
1925           already present by accident
1926       */
1927       if (s[0] == 'E' && s[1] == 'x'  && s[2] == 'i' &&
1928           s[3] == 'f' && s[4] == '\0' && s[5] == '\0')
1929       {
1930         s+=6;
1931         i=6;
1932         SetStringInfoLength(profile,size);
1933         p=GetStringInfoDatum(profile);
1934       }
1935     }
1936 
1937   /* copy chunk->data to profile */
1938   for (; i<size; i++)
1939     *p++ = *s++;
1940 
1941   (void) SetImageProfile(image,"exif",profile,exception);
1942 
1943   profile=DestroyStringInfo(profile);
1944 
1945   return(1);
1946 }
1947 
1948 #if defined(PNG_READ_eXIf_SUPPORTED)
read_eXIf_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)1949 static void read_eXIf_chunk(Image *image,png_struct *ping,png_info *info,
1950   ExceptionInfo *exception)
1951 {
1952   png_uint_32
1953     size;
1954 
1955   png_bytep
1956     data;
1957 
1958 #if PNG_LIBPNG_VER > 10631
1959   if (png_get_eXIf_1(ping,info,&size,&data))
1960     (void) PNGSetExifProfile(image,size,data,exception);
1961 #endif
1962 }
1963 #endif
1964 
1965 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1966 
read_user_chunk_callback(png_struct * ping,png_unknown_chunkp chunk)1967 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1968 {
1969   Image
1970     *image;
1971 
1972   PNGErrorInfo
1973     *error_info;
1974 
1975   /* The unknown chunk structure contains the chunk data:
1976      png_byte name[5];
1977      png_byte *data;
1978      png_size_t size;
1979 
1980      Note that libpng has already taken care of the CRC handling.
1981 
1982      Returns one of the following:
1983          return(-n);  chunk had an error
1984          return(0);  did not recognize
1985          return(n);  success
1986   */
1987 
1988   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1989      "    read_user_chunk: found %c%c%c%c chunk",
1990        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1991 
1992   if (chunk->name[0]  == 101 &&
1993       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
1994       chunk->name[2] ==   73 &&
1995       chunk-> name[3] == 102)
1996     {
1997       /* process eXIf or exIf chunk */
1998 
1999       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2000         " recognized eXIf chunk");
2001 
2002       image=(Image *) png_get_user_chunk_ptr(ping);
2003 
2004       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2005 
2006       return(PNGSetExifProfile(image,chunk->size,chunk->data,
2007         error_info->exception));
2008     }
2009 
2010   /* orNT */
2011   if (chunk->name[0] == 111 &&
2012       chunk->name[1] == 114 &&
2013       chunk->name[2] ==  78 &&
2014       chunk->name[3] ==  84)
2015     {
2016      /* recognized orNT */
2017      if (chunk->size != 1)
2018        return(-1); /* Error return */
2019 
2020      image=(Image *) png_get_user_chunk_ptr(ping);
2021 
2022      image->orientation=
2023        Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
2024 
2025      return(1);
2026     }
2027 
2028   /* vpAg (deprecated, replaced by caNv) */
2029   if (chunk->name[0] == 118 &&
2030       chunk->name[1] == 112 &&
2031       chunk->name[2] ==  65 &&
2032       chunk->name[3] == 103)
2033     {
2034       /* recognized vpAg */
2035 
2036       if (chunk->size != 9)
2037         return(-1); /* Error return */
2038 
2039       if (chunk->data[8] != 0)
2040         return(0);  /* ImageMagick requires pixel units */
2041 
2042       image=(Image *) png_get_user_chunk_ptr(ping);
2043 
2044       image->page.width=(size_t)mng_get_long(chunk->data);
2045       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2046 
2047       return(1);
2048     }
2049 
2050   /* caNv */
2051   if (chunk->name[0] ==  99 &&
2052       chunk->name[1] ==  97 &&
2053       chunk->name[2] ==  78 &&
2054       chunk->name[3] == 118)
2055     {
2056       /* recognized caNv */
2057 
2058       if (chunk->size != 16)
2059         return(-1); /* Error return */
2060 
2061       image=(Image *) png_get_user_chunk_ptr(ping);
2062 
2063       image->page.width=(size_t) mng_get_long(chunk->data);
2064       image->page.height=(size_t) mng_get_long(&chunk->data[4]);
2065       image->page.x=(ssize_t) ((int) mng_get_long(&chunk->data[8]));
2066       image->page.y=(ssize_t) ((int) mng_get_long(&chunk->data[12]));
2067 
2068       return(1);
2069     }
2070 
2071   /* acTL */
2072   if ((chunk->name[0]  == 97) && (chunk->name[1]  == 99) &&
2073       (chunk->name[2]  == 84) && (chunk->name[3]  == 76))
2074     {
2075       image=(Image *) png_get_user_chunk_ptr(ping);
2076       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2077 
2078       (void) SetImageProperty(image,"png:acTL","chunk was found",
2079         error_info->exception);
2080 
2081       return(1);
2082     }
2083 
2084   return(0); /* Did not recognize */
2085 }
2086 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2087 
2088 #if defined(PNG_tIME_SUPPORTED)
read_tIME_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)2089 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2090   ExceptionInfo *exception)
2091 {
2092   png_timep
2093     time;
2094 
2095   if (png_get_tIME(ping,info,&time))
2096     {
2097       char
2098         timestamp[21];
2099 
2100       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2101         time->year,time->month,time->day,time->hour,time->minute,time->second);
2102       SetImageProperty(image,"png:tIME",timestamp,exception);
2103     }
2104 }
2105 #endif
2106 
2107 /*
2108 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2109 %                                                                             %
2110 %                                                                             %
2111 %                                                                             %
2112 %   R e a d O n e P N G I m a g e                                             %
2113 %                                                                             %
2114 %                                                                             %
2115 %                                                                             %
2116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2117 %
2118 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2119 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2120 %  necessary for the new Image structure and returns a pointer to the new
2121 %  image.
2122 %
2123 %  The format of the ReadOnePNGImage method is:
2124 %
2125 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2126 %         ExceptionInfo *exception)
2127 %
2128 %  A description of each parameter follows:
2129 %
2130 %    o mng_info: Specifies a pointer to a MngInfo structure.
2131 %
2132 %    o image_info: the image info.
2133 %
2134 %    o exception: return any errors or warnings in this structure.
2135 %
2136 */
ReadOnePNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)2137 static Image *ReadOnePNGImage(MngInfo *mng_info,
2138     const ImageInfo *image_info, ExceptionInfo *exception)
2139 {
2140   /* Read one PNG image */
2141 
2142   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2143 
2144   Image
2145     *image;
2146 
2147   char
2148     im_vers[32],
2149     libpng_runv[32],
2150     libpng_vers[32],
2151     zlib_runv[32],
2152     zlib_vers[32];
2153 
2154   int
2155     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2156     num_raw_profiles,
2157     num_text,
2158     num_text_total,
2159     num_passes,
2160     number_colors,
2161     pass,
2162     ping_bit_depth,
2163     ping_color_type,
2164     ping_file_depth,
2165     ping_interlace_method,
2166     ping_compression_method,
2167     ping_filter_method,
2168     ping_num_trans,
2169     unit_type;
2170 
2171   double
2172     file_gamma;
2173 
2174   MagickBooleanType
2175     logging,
2176     ping_found_cHRM,
2177     ping_found_gAMA,
2178     ping_found_iCCP,
2179     ping_found_sRGB,
2180     ping_found_sRGB_cHRM,
2181     ping_preserve_iCCP,
2182     status;
2183 
2184   MemoryInfo
2185     *volatile pixel_info;
2186 
2187   PixelInfo
2188     transparent_color;
2189 
2190   PNGErrorInfo
2191     error_info;
2192 
2193   png_bytep
2194      ping_trans_alpha;
2195 
2196   png_color_16p
2197      ping_background,
2198      ping_trans_color;
2199 
2200   png_info
2201     *end_info,
2202     *ping_info;
2203 
2204   png_struct
2205     *ping;
2206 
2207   png_textp
2208     text;
2209 
2210   png_uint_32
2211     ping_height,
2212     ping_width,
2213     x_resolution,
2214     y_resolution;
2215 
2216   QuantumInfo
2217     *volatile quantum_info;
2218 
2219   Quantum
2220     *volatile quantum_scanline;
2221 
2222   ssize_t
2223     ping_rowbytes,
2224     y;
2225 
2226   register unsigned char
2227     *p;
2228 
2229   register ssize_t
2230     i,
2231     x;
2232 
2233   register Quantum
2234     *q;
2235 
2236   size_t
2237     length,
2238     row_offset;
2239 
2240   ssize_t
2241     j;
2242 
2243   unsigned char
2244     *ping_pixels;
2245 
2246 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2247   png_byte unused_chunks[]=
2248   {
2249     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2250     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2251     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2252     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2253     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2254 #if !defined(PNG_tIME_SUPPORTED)
2255     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2256 #endif
2257 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2258                           /* ignore the APNG chunks */
2259      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2260     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2261     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2262 #endif
2263   };
2264 #endif
2265 
2266   /* Define these outside of the following "if logging()" block so they will
2267    * show in debuggers.
2268    */
2269   *im_vers='\0';
2270   (void) ConcatenateMagickString(im_vers,
2271          MagickLibVersionText,32);
2272   (void) ConcatenateMagickString(im_vers,
2273          MagickLibAddendum,32);
2274 
2275   *libpng_vers='\0';
2276   (void) ConcatenateMagickString(libpng_vers,
2277          PNG_LIBPNG_VER_STRING,32);
2278   *libpng_runv='\0';
2279   (void) ConcatenateMagickString(libpng_runv,
2280          png_get_libpng_ver(NULL),32);
2281 
2282   *zlib_vers='\0';
2283   (void) ConcatenateMagickString(zlib_vers,
2284          ZLIB_VERSION,32);
2285   *zlib_runv='\0';
2286   (void) ConcatenateMagickString(zlib_runv,
2287          zlib_version,32);
2288 
2289   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2290        "  Enter ReadOnePNGImage()\n"
2291        "    IM version     = %s\n"
2292        "    Libpng version = %s",
2293        im_vers, libpng_vers);
2294 
2295   if (logging != MagickFalse)
2296   {
2297     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2298     {
2299    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2300       "      running with   %s", libpng_runv);
2301     }
2302     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2303         "    Zlib version   = %s", zlib_vers);
2304     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2305     {
2306     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2307         "      running with   %s", zlib_runv);
2308     }
2309   }
2310 
2311 #if (PNG_LIBPNG_VER < 10200)
2312   if (image_info->verbose)
2313     printf("Your PNG library (libpng-%s) is rather old.\n",
2314        PNG_LIBPNG_VER_STRING);
2315 #endif
2316 
2317 #if (PNG_LIBPNG_VER >= 10400)
2318 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2319   if (image_info->verbose)
2320     {
2321       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2322            PNG_LIBPNG_VER_STRING);
2323       printf("Please update it.\n");
2324     }
2325 #  endif
2326 #endif
2327 
2328 
2329   quantum_info = (QuantumInfo *) NULL;
2330   image=mng_info->image;
2331 
2332   if (logging != MagickFalse)
2333   {
2334     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2335        "    Before reading:\n"
2336        "      image->alpha_trait=%d\n"
2337        "      image->rendering_intent=%d\n"
2338        "      image->colorspace=%d\n"
2339        "      image->gamma=%f",
2340        (int) image->alpha_trait, (int) image->rendering_intent,
2341        (int) image->colorspace, image->gamma);
2342   }
2343   intent=
2344     Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2345 
2346   /* Set to an out-of-range color unless tRNS chunk is present */
2347   transparent_color.red=65537;
2348   transparent_color.green=65537;
2349   transparent_color.blue=65537;
2350   transparent_color.alpha=65537;
2351 
2352   number_colors=0;
2353   num_text = 0;
2354   num_text_total = 0;
2355   num_raw_profiles = 0;
2356 
2357   ping_found_cHRM = MagickFalse;
2358   ping_found_gAMA = MagickFalse;
2359   ping_found_iCCP = MagickFalse;
2360   ping_found_sRGB = MagickFalse;
2361   ping_found_sRGB_cHRM = MagickFalse;
2362   ping_preserve_iCCP = MagickFalse;
2363 
2364 
2365   /*
2366     Allocate the PNG structures
2367   */
2368 #ifdef PNG_USER_MEM_SUPPORTED
2369  error_info.image=image;
2370  error_info.exception=exception;
2371  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2372    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2373    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2374 #else
2375   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2376     MagickPNGErrorHandler,MagickPNGWarningHandler);
2377 #endif
2378   if (ping == (png_struct *) NULL)
2379     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2380 
2381   ping_info=png_create_info_struct(ping);
2382 
2383   if (ping_info == (png_info *) NULL)
2384     {
2385       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2386       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2387     }
2388 
2389   end_info=png_create_info_struct(ping);
2390 
2391   if (end_info == (png_info *) NULL)
2392     {
2393       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2394       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2395     }
2396 
2397   pixel_info=(MemoryInfo *) NULL;
2398   quantum_scanline = (Quantum *) NULL;
2399   quantum_info = (QuantumInfo *) NULL;
2400 
2401   if (setjmp(png_jmpbuf(ping)))
2402     {
2403       /*
2404         PNG image is corrupt.
2405       */
2406       png_destroy_read_struct(&ping,&ping_info,&end_info);
2407 
2408       if (pixel_info != (MemoryInfo *) NULL)
2409         pixel_info=RelinquishVirtualMemory(pixel_info);
2410 
2411       if (quantum_info != (QuantumInfo *) NULL)
2412         quantum_info=DestroyQuantumInfo(quantum_info);
2413 
2414       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2415 
2416 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2417       UnlockSemaphoreInfo(ping_semaphore);
2418 #endif
2419 
2420       if (logging != MagickFalse)
2421         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2422           "  exit ReadOnePNGImage() with error.");
2423 
2424       if (image != (Image *) NULL)
2425         image=DestroyImageList(image);
2426       return(image);
2427     }
2428 
2429   /* {  For navigation to end of SETJMP-protected block.  Within this
2430    *    block, use png_error() instead of Throwing an Exception, to ensure
2431    *    that libpng is able to clean up, and that the semaphore is unlocked.
2432    */
2433 
2434 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2435   LockSemaphoreInfo(ping_semaphore);
2436 #endif
2437 
2438 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2439   /* Allow benign errors */
2440   png_set_benign_errors(ping, 1);
2441 #endif
2442 
2443 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2444   {
2445     const char
2446       *option;
2447 
2448     /* Reject images with too many rows or columns */
2449     png_set_user_limits(ping,(png_uint_32) MagickMin(PNG_UINT_31_MAX,
2450       GetMagickResourceLimit(WidthResource)),(png_uint_32)
2451       MagickMin(PNG_UINT_31_MAX,GetMagickResourceLimit(HeightResource)));
2452 
2453 #if (PNG_LIBPNG_VER >= 10400)
2454     option=GetImageOption(image_info,"png:chunk-cache-max");
2455     if (option != (const char *) NULL)
2456       png_set_chunk_cache_max(ping,(png_uint_32) MagickMin(PNG_UINT_32_MAX,
2457         StringToLong(option)));
2458     else
2459       png_set_chunk_cache_max(ping,32767);
2460 #endif
2461 
2462 #if (PNG_LIBPNG_VER >= 10401)
2463     option=GetImageOption(image_info,"png:chunk-malloc-max");
2464     if (option != (const char *) NULL)
2465       png_set_chunk_malloc_max(ping,(png_alloc_size_t) MagickMin(PNG_SIZE_MAX,
2466         (size_t) StringToLong(option)));
2467     else
2468       png_set_chunk_malloc_max(ping,(png_alloc_size_t) GetMaxMemoryRequest());
2469 #endif
2470   }
2471 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2472 
2473   /*
2474     Prepare PNG for reading.
2475   */
2476 
2477   mng_info->image_found++;
2478   png_set_sig_bytes(ping,8);
2479 
2480   if (LocaleCompare(image_info->magick,"MNG") == 0)
2481     {
2482 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2483       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2484       png_set_read_fn(ping,image,png_get_data);
2485 #else
2486 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2487       png_permit_empty_plte(ping,MagickTrue);
2488       png_set_read_fn(ping,image,png_get_data);
2489 #else
2490       mng_info->image=image;
2491       mng_info->bytes_in_read_buffer=0;
2492       mng_info->found_empty_plte=MagickFalse;
2493       mng_info->have_saved_bkgd_index=MagickFalse;
2494       png_set_read_fn(ping,mng_info,mng_get_data);
2495 #endif
2496 #endif
2497     }
2498 
2499   else
2500     png_set_read_fn(ping,image,png_get_data);
2501 
2502   {
2503     const char
2504       *value;
2505 
2506     value=GetImageOption(image_info,"png:ignore-crc");
2507     if (value != NULL)
2508     {
2509        /* Turn off CRC checking while reading */
2510        png_set_crc_action(ping, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
2511 #ifdef PNG_IGNORE_ADLER32
2512        /* Turn off ADLER32 checking while reading */
2513        png_set_option(ping, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
2514 #endif
2515      }
2516 
2517     value=GetImageOption(image_info,"profile:skip");
2518 
2519     if (IsOptionMember("ICC",value) == MagickFalse)
2520     {
2521 
2522        value=GetImageOption(image_info,"png:preserve-iCCP");
2523 
2524        if (value == NULL)
2525           value=GetImageArtifact(image,"png:preserve-iCCP");
2526 
2527        if (value != NULL)
2528           ping_preserve_iCCP=MagickTrue;
2529 
2530 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2531        /* Don't let libpng check for ICC/sRGB profile because we're going
2532         * to do that anyway.  This feature was added at libpng-1.6.12.
2533         * If logging, go ahead and check and issue a warning as appropriate.
2534         */
2535        if (logging == MagickFalse)
2536           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2537 #endif
2538     }
2539 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2540     else
2541     {
2542        png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2543     }
2544 #endif
2545   }
2546 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2547   /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2548 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2549   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2550 # else
2551   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2552 # endif
2553   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2554   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2555   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2556      (int)sizeof(unused_chunks)/5);
2557   /* Callback for other unknown chunks */
2558   png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2559 #endif
2560 
2561 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2562     /* Disable new libpng-1.5.10 feature */
2563     png_set_check_for_invalid_index (ping, 0);
2564 #endif
2565 
2566 #if (PNG_LIBPNG_VER < 10400)
2567 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2568    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2569   /* Disable thread-unsafe features of pnggccrd */
2570   if (png_access_version_number() >= 10200)
2571   {
2572     png_uint_32 mmx_disable_mask=0;
2573     png_uint_32 asm_flags;
2574 
2575     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2576                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2577                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2578                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2579     asm_flags=png_get_asm_flags(ping);
2580     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2581   }
2582 #  endif
2583 #endif
2584 
2585   png_read_info(ping,ping_info);
2586 
2587   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2588                &ping_bit_depth,&ping_color_type,
2589                &ping_interlace_method,&ping_compression_method,
2590                &ping_filter_method);
2591 
2592   ping_file_depth = ping_bit_depth;
2593 
2594   /* Swap bytes if requested */
2595   if (ping_file_depth == 16)
2596   {
2597      const char
2598        *value;
2599 
2600      value=GetImageOption(image_info,"png:swap-bytes");
2601 
2602      if (value == NULL)
2603         value=GetImageArtifact(image,"png:swap-bytes");
2604 
2605      if (value != NULL)
2606         png_set_swap(ping);
2607   }
2608 
2609   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2610   {
2611       char
2612         msg[MagickPathExtent];
2613 
2614       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2615          (int) ping_color_type);
2616       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2617 
2618       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2619          (int) ping_bit_depth);
2620       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2621   }
2622 
2623   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2624                       &ping_trans_color);
2625 
2626   (void) png_get_bKGD(ping, ping_info, &ping_background);
2627 
2628   if (ping_bit_depth < 8)
2629     {
2630        png_set_packing(ping);
2631        ping_bit_depth = 8;
2632     }
2633 
2634   image->depth=ping_bit_depth;
2635   image->depth=GetImageQuantumDepth(image,MagickFalse);
2636   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2637 
2638   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2639       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2640     {
2641       image->rendering_intent=UndefinedIntent;
2642       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2643       (void) memset(&image->chromaticity,0,
2644         sizeof(image->chromaticity));
2645     }
2646 
2647   if (logging != MagickFalse)
2648     {
2649       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2650         "    PNG width: %.20g, height: %.20g\n"
2651         "    PNG color_type: %d, bit_depth: %d\n"
2652         "    PNG compression_method: %d\n"
2653         "    PNG interlace_method: %d, filter_method: %d",
2654         (double) ping_width, (double) ping_height,
2655         ping_color_type, ping_bit_depth,
2656         ping_compression_method,
2657         ping_interlace_method,ping_filter_method);
2658 
2659     }
2660 
2661   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2662     {
2663       ping_found_iCCP=MagickTrue;
2664       if (logging != MagickFalse)
2665         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2666           "    Found PNG iCCP chunk.");
2667     }
2668 
2669   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2670     {
2671       ping_found_gAMA=MagickTrue;
2672       if (logging != MagickFalse)
2673         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2674           "    Found PNG gAMA chunk.");
2675     }
2676 
2677   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2678     {
2679       ping_found_cHRM=MagickTrue;
2680       if (logging != MagickFalse)
2681         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2682           "    Found PNG cHRM chunk.");
2683     }
2684 
2685   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2686       PNG_INFO_sRGB))
2687     {
2688       ping_found_sRGB=MagickTrue;
2689       if (logging != MagickFalse)
2690         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2691           "    Found PNG sRGB chunk.");
2692     }
2693 
2694 #ifdef PNG_READ_iCCP_SUPPORTED
2695     if (ping_found_iCCP !=MagickTrue &&
2696       ping_found_sRGB != MagickTrue &&
2697       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2698     {
2699       ping_found_iCCP=MagickTrue;
2700       if (logging != MagickFalse)
2701         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2702           "    Found PNG iCCP chunk.");
2703     }
2704 
2705   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2706     {
2707       int
2708         compression;
2709 
2710 #if (PNG_LIBPNG_VER < 10500)
2711       png_charp
2712         info;
2713 #else
2714       png_bytep
2715         info;
2716 #endif
2717 
2718       png_charp
2719         name;
2720 
2721       png_uint_32
2722         profile_length;
2723 
2724       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2725         &profile_length);
2726 
2727       if (profile_length != 0)
2728         {
2729           StringInfo
2730             *profile;
2731 
2732           if (logging != MagickFalse)
2733             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734               "    Reading PNG iCCP chunk.");
2735 
2736           profile=BlobToStringInfo(info,(const size_t) profile_length);
2737 
2738           if (profile == (StringInfo *) NULL)
2739           {
2740             png_warning(ping, "ICC profile is NULL");
2741             profile=DestroyStringInfo(profile);
2742           }
2743           else
2744           {
2745             if (ping_preserve_iCCP == MagickFalse)
2746             {
2747                  int
2748                    icheck,
2749                    got_crc=0;
2750 
2751 
2752                  png_uint_32
2753                    profile_crc=0;
2754 
2755                  unsigned char
2756                    *data;
2757 
2758                  profile_length=(png_uint_32) GetStringInfoLength(profile);
2759 
2760                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2761                  {
2762                    if (profile_length == sRGB_info[icheck].len)
2763                    {
2764                      if (got_crc == 0)
2765                      {
2766                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2767                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2768                          (unsigned long) profile_length);
2769 
2770                        data=GetStringInfoDatum(profile);
2771                        profile_crc=crc32(0,data,profile_length);
2772 
2773                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2774                            "      with crc=%8x",(unsigned int) profile_crc);
2775                        got_crc++;
2776                      }
2777 
2778                      if (profile_crc == sRGB_info[icheck].crc)
2779                      {
2780                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2781                             "      It is sRGB with rendering intent = %s",
2782                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2783                              sRGB_info[icheck].intent));
2784                         if (image->rendering_intent==UndefinedIntent)
2785                         {
2786                           image->rendering_intent=
2787                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2788                              sRGB_info[icheck].intent);
2789                         }
2790                         break;
2791                      }
2792                    }
2793                  }
2794                  if (sRGB_info[icheck].len == 0)
2795                  {
2796                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2797                         "    Got %lu-byte ICC profile not recognized as sRGB",
2798                         (unsigned long) profile_length);
2799                     (void) SetImageProfile(image,"icc",profile,exception);
2800                  }
2801             }
2802             else /* Preserve-iCCP */
2803             {
2804                     (void) SetImageProfile(image,"icc",profile,exception);
2805             }
2806 
2807             profile=DestroyStringInfo(profile);
2808           }
2809       }
2810     }
2811 #endif
2812 
2813 #if defined(PNG_READ_sRGB_SUPPORTED)
2814   {
2815     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2816         PNG_INFO_sRGB))
2817     {
2818       if (png_get_sRGB(ping,ping_info,&intent))
2819       {
2820         if (image->rendering_intent == UndefinedIntent)
2821           image->rendering_intent=
2822              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2823 
2824         if (logging != MagickFalse)
2825           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2826             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2827       }
2828     }
2829 
2830     else if (mng_info->have_global_srgb)
2831       {
2832         if (image->rendering_intent == UndefinedIntent)
2833           image->rendering_intent=
2834             Magick_RenderingIntent_from_PNG_RenderingIntent
2835             (mng_info->global_srgb_intent);
2836       }
2837   }
2838 #endif
2839 
2840 
2841   {
2842      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2843        if (mng_info->have_global_gama)
2844          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2845 
2846      if (png_get_gAMA(ping,ping_info,&file_gamma))
2847        {
2848          image->gamma=(float) file_gamma;
2849          if (logging != MagickFalse)
2850            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2851              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2852        }
2853   }
2854 
2855   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2856     {
2857       if (mng_info->have_global_chrm != MagickFalse)
2858         {
2859           (void) png_set_cHRM(ping,ping_info,
2860             mng_info->global_chrm.white_point.x,
2861             mng_info->global_chrm.white_point.y,
2862             mng_info->global_chrm.red_primary.x,
2863             mng_info->global_chrm.red_primary.y,
2864             mng_info->global_chrm.green_primary.x,
2865             mng_info->global_chrm.green_primary.y,
2866             mng_info->global_chrm.blue_primary.x,
2867             mng_info->global_chrm.blue_primary.y);
2868         }
2869     }
2870 
2871   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2872     {
2873       (void) png_get_cHRM(ping,ping_info,
2874         &image->chromaticity.white_point.x,
2875         &image->chromaticity.white_point.y,
2876         &image->chromaticity.red_primary.x,
2877         &image->chromaticity.red_primary.y,
2878         &image->chromaticity.green_primary.x,
2879         &image->chromaticity.green_primary.y,
2880         &image->chromaticity.blue_primary.x,
2881         &image->chromaticity.blue_primary.y);
2882 
2883        ping_found_cHRM=MagickTrue;
2884 
2885        if (image->chromaticity.red_primary.x>0.6399f &&
2886            image->chromaticity.red_primary.x<0.6401f &&
2887            image->chromaticity.red_primary.y>0.3299f &&
2888            image->chromaticity.red_primary.y<0.3301f &&
2889            image->chromaticity.green_primary.x>0.2999f &&
2890            image->chromaticity.green_primary.x<0.3001f &&
2891            image->chromaticity.green_primary.y>0.5999f &&
2892            image->chromaticity.green_primary.y<0.6001f &&
2893            image->chromaticity.blue_primary.x>0.1499f &&
2894            image->chromaticity.blue_primary.x<0.1501f &&
2895            image->chromaticity.blue_primary.y>0.0599f &&
2896            image->chromaticity.blue_primary.y<0.0601f &&
2897            image->chromaticity.white_point.x>0.3126f &&
2898            image->chromaticity.white_point.x<0.3128f &&
2899            image->chromaticity.white_point.y>0.3289f &&
2900            image->chromaticity.white_point.y<0.3291f)
2901           ping_found_sRGB_cHRM=MagickTrue;
2902     }
2903 
2904   if (image->rendering_intent != UndefinedIntent)
2905     {
2906       if (ping_found_sRGB != MagickTrue &&
2907           (ping_found_gAMA != MagickTrue ||
2908           (image->gamma > .45 && image->gamma < .46)) &&
2909           (ping_found_cHRM != MagickTrue ||
2910           ping_found_sRGB_cHRM != MagickFalse) &&
2911           ping_found_iCCP != MagickTrue)
2912       {
2913          png_set_sRGB(ping,ping_info,
2914             Magick_RenderingIntent_to_PNG_RenderingIntent
2915             (image->rendering_intent));
2916          file_gamma=0.45455f;
2917          ping_found_sRGB=MagickTrue;
2918          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2919            "    Setting sRGB as if in input");
2920       }
2921     }
2922 
2923 #if defined(PNG_oFFs_SUPPORTED)
2924   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2925     {
2926       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2927       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2928 
2929       if (logging != MagickFalse)
2930         if (image->page.x || image->page.y)
2931           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2932             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2933             image->page.x,(double) image->page.y);
2934     }
2935 #endif
2936 #if defined(PNG_pHYs_SUPPORTED)
2937   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2938     {
2939       if (mng_info->have_global_phys)
2940         {
2941           png_set_pHYs(ping,ping_info,
2942                        mng_info->global_x_pixels_per_unit,
2943                        mng_info->global_y_pixels_per_unit,
2944                        mng_info->global_phys_unit_type);
2945         }
2946     }
2947 
2948   x_resolution=0;
2949   y_resolution=0;
2950   unit_type=0;
2951   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2952     {
2953       /*
2954         Set image resolution.
2955       */
2956       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2957         &unit_type);
2958       image->resolution.x=(double) x_resolution;
2959       image->resolution.y=(double) y_resolution;
2960 
2961       if (unit_type == PNG_RESOLUTION_METER)
2962         {
2963           image->units=PixelsPerCentimeterResolution;
2964           image->resolution.x=(double) x_resolution/100.0;
2965           image->resolution.y=(double) y_resolution/100.0;
2966         }
2967 
2968       if (logging != MagickFalse)
2969         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2970           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2971           (double) x_resolution,(double) y_resolution,unit_type);
2972     }
2973 #endif
2974 
2975   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2976     {
2977       png_colorp
2978         palette;
2979 
2980       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2981 
2982       if ((number_colors == 0) &&
2983           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2984         {
2985           if (mng_info->global_plte_length)
2986             {
2987               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2988                 (int) mng_info->global_plte_length);
2989 
2990               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2991               {
2992                 if (mng_info->global_trns_length)
2993                   {
2994                     png_warning(ping,
2995                       "global tRNS has more entries than global PLTE");
2996                   }
2997                 else
2998                   {
2999                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
3000                        (int) mng_info->global_trns_length,NULL);
3001                   }
3002                }
3003 #ifdef PNG_READ_bKGD_SUPPORTED
3004               if (
3005 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3006                    mng_info->have_saved_bkgd_index ||
3007 #endif
3008                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3009                     {
3010                       png_color_16
3011                          background;
3012 
3013 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3014                       if (mng_info->have_saved_bkgd_index)
3015                         background.index=mng_info->saved_bkgd_index;
3016 #endif
3017                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3018                         background.index=ping_background->index;
3019 
3020                       background.red=(png_uint_16)
3021                         mng_info->global_plte[background.index].red;
3022 
3023                       background.green=(png_uint_16)
3024                         mng_info->global_plte[background.index].green;
3025 
3026                       background.blue=(png_uint_16)
3027                         mng_info->global_plte[background.index].blue;
3028 
3029                       background.gray=(png_uint_16)
3030                         mng_info->global_plte[background.index].green;
3031 
3032                       png_set_bKGD(ping,ping_info,&background);
3033                     }
3034 #endif
3035                 }
3036               else
3037                 png_error(ping,"No global PLTE in file");
3038             }
3039         }
3040 
3041 #ifdef PNG_READ_bKGD_SUPPORTED
3042   if (mng_info->have_global_bkgd &&
3043           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3044       image->background_color=mng_info->mng_global_bkgd;
3045 
3046   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3047     {
3048       unsigned int
3049         bkgd_scale;
3050 
3051       /* Set image background color.
3052        * Scale background components to 16-bit, then scale
3053        * to quantum depth
3054        */
3055 
3056         bkgd_scale = 1;
3057 
3058         if (ping_file_depth == 1)
3059            bkgd_scale = 255;
3060 
3061         else if (ping_file_depth == 2)
3062            bkgd_scale = 85;
3063 
3064         else if (ping_file_depth == 4)
3065            bkgd_scale = 17;
3066 
3067         if (ping_file_depth <= 8)
3068            bkgd_scale *= 257;
3069 
3070         ping_background->red *= bkgd_scale;
3071         ping_background->green *= bkgd_scale;
3072         ping_background->blue *= bkgd_scale;
3073 
3074         if (logging != MagickFalse)
3075           {
3076             if (logging != MagickFalse)
3077               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3078                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3079                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d)",
3080                  ping_background->red,ping_background->green,
3081                  ping_background->blue,
3082                  bkgd_scale,ping_background->red,
3083                  ping_background->green,ping_background->blue);
3084           }
3085 
3086         image->background_color.red=
3087             ScaleShortToQuantum(ping_background->red);
3088 
3089         image->background_color.green=
3090             ScaleShortToQuantum(ping_background->green);
3091 
3092         image->background_color.blue=
3093           ScaleShortToQuantum(ping_background->blue);
3094 
3095         image->background_color.alpha=OpaqueAlpha;
3096 
3097         if (logging != MagickFalse)
3098           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3099             "    image->background_color=(%.20g,%.20g,%.20g).",
3100             (double) image->background_color.red,
3101             (double) image->background_color.green,
3102             (double) image->background_color.blue);
3103     }
3104 #endif /* PNG_READ_bKGD_SUPPORTED */
3105 
3106   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3107     {
3108       /*
3109         Image has a tRNS chunk.
3110       */
3111       int
3112         max_sample;
3113 
3114       size_t
3115         one = 1;
3116 
3117       if (logging != MagickFalse)
3118         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3119           "    Reading PNG tRNS chunk.");
3120 
3121       max_sample = (int) ((one << ping_file_depth) - 1);
3122 
3123       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3124           (int)ping_trans_color->gray > max_sample) ||
3125           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3126           ((int)ping_trans_color->red > max_sample ||
3127           (int)ping_trans_color->green > max_sample ||
3128           (int)ping_trans_color->blue > max_sample)))
3129         {
3130           if (logging != MagickFalse)
3131             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3132               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3133           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3134           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3135           image->alpha_trait=UndefinedPixelTrait;
3136         }
3137       else
3138         {
3139           int
3140             scale_to_short;
3141 
3142           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3143 
3144           /* Scale transparent_color to short */
3145           transparent_color.red= scale_to_short*ping_trans_color->red;
3146           transparent_color.green= scale_to_short*ping_trans_color->green;
3147           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3148           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3149 
3150           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3151             {
3152               if (logging != MagickFalse)
3153               {
3154                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3155                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3156                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3157 
3158               }
3159               transparent_color.red=transparent_color.alpha;
3160               transparent_color.green=transparent_color.alpha;
3161               transparent_color.blue=transparent_color.alpha;
3162             }
3163         }
3164     }
3165 #if defined(PNG_READ_sBIT_SUPPORTED)
3166   if (mng_info->have_global_sbit)
3167     {
3168       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3169         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3170     }
3171 #endif
3172   num_passes=png_set_interlace_handling(ping);
3173 
3174   png_read_update_info(ping,ping_info);
3175 
3176   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3177 
3178   /*
3179     Initialize image structure.
3180   */
3181   mng_info->image_box.left=0;
3182   mng_info->image_box.right=(ssize_t) ping_width;
3183   mng_info->image_box.top=0;
3184   mng_info->image_box.bottom=(ssize_t) ping_height;
3185   if (mng_info->mng_type == 0)
3186     {
3187       mng_info->mng_width=ping_width;
3188       mng_info->mng_height=ping_height;
3189       mng_info->frame=mng_info->image_box;
3190       mng_info->clip=mng_info->image_box;
3191     }
3192 
3193   else
3194     {
3195       image->page.y=mng_info->y_off[mng_info->object_id];
3196     }
3197 
3198   image->compression=ZipCompression;
3199   image->columns=ping_width;
3200   image->rows=ping_height;
3201 
3202   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3203       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3204     {
3205       double
3206         image_gamma = image->gamma;
3207 
3208       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3209          "    image->gamma=%f",(float) image_gamma);
3210 
3211       if (image_gamma > 0.75)
3212         {
3213           /* Set image->rendering_intent to Undefined,
3214            * image->colorspace to GRAY, and reset image->chromaticity.
3215            */
3216           image->intensity = Rec709LuminancePixelIntensityMethod;
3217           SetImageColorspace(image,LinearGRAYColorspace,exception);
3218         }
3219       else
3220         {
3221           RenderingIntent
3222             save_rendering_intent = image->rendering_intent;
3223           ChromaticityInfo
3224             save_chromaticity = image->chromaticity;
3225 
3226           SetImageColorspace(image,GRAYColorspace,exception);
3227           image->rendering_intent = save_rendering_intent;
3228           image->chromaticity = save_chromaticity;
3229         }
3230 
3231       image->gamma = image_gamma;
3232     }
3233   else
3234     {
3235       double
3236         image_gamma = image->gamma;
3237 
3238       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3239          "    image->gamma=%f",(float) image_gamma);
3240 
3241       if (image_gamma > 0.75)
3242         {
3243           /* Set image->rendering_intent to Undefined,
3244            * image->colorspace to GRAY, and reset image->chromaticity.
3245            */
3246           image->intensity = Rec709LuminancePixelIntensityMethod;
3247           SetImageColorspace(image,RGBColorspace,exception);
3248         }
3249       else
3250         {
3251           RenderingIntent
3252             save_rendering_intent = image->rendering_intent;
3253           ChromaticityInfo
3254             save_chromaticity = image->chromaticity;
3255 
3256           SetImageColorspace(image,sRGBColorspace,exception);
3257           image->rendering_intent = save_rendering_intent;
3258           image->chromaticity = save_chromaticity;
3259         }
3260 
3261       image->gamma = image_gamma;
3262     }
3263 
3264   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3265       "    image->colorspace=%d",(int) image->colorspace);
3266 
3267   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3268       ((int) ping_bit_depth < 16 &&
3269       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3270     {
3271       size_t
3272         one;
3273 
3274       image->storage_class=PseudoClass;
3275       one=1;
3276       image->colors=one << ping_file_depth;
3277 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3278       if (image->colors > 256)
3279         image->colors=256;
3280 #else
3281       if (image->colors > 65536L)
3282         image->colors=65536L;
3283 #endif
3284       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3285         {
3286           png_colorp
3287             palette;
3288 
3289           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3290           image->colors=(size_t) number_colors;
3291 
3292           if (logging != MagickFalse)
3293             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3294               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3295         }
3296     }
3297 
3298   if (image->storage_class == PseudoClass)
3299     {
3300       /*
3301         Initialize image colormap.
3302       */
3303       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3304         png_error(ping,"Memory allocation failed");
3305 
3306       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3307         {
3308           png_colorp
3309             palette;
3310 
3311           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3312 
3313           for (i=0; i < (ssize_t) number_colors; i++)
3314           {
3315             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3316             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3317             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3318           }
3319 
3320           for ( ; i < (ssize_t) image->colors; i++)
3321           {
3322             image->colormap[i].red=0;
3323             image->colormap[i].green=0;
3324             image->colormap[i].blue=0;
3325           }
3326         }
3327     }
3328 
3329    /* Set some properties for reporting by "identify" */
3330     {
3331       char
3332         msg[MagickPathExtent];
3333 
3334      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3335         ping_interlace_method in value */
3336 
3337      (void) FormatLocaleString(msg,MagickPathExtent,
3338          "%d, %d",(int) ping_width, (int) ping_height);
3339      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3340 
3341      (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3342         (int) ping_file_depth);
3343      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3344 
3345      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3346          (int) ping_color_type,
3347          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3348      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3349 
3350      if (ping_interlace_method == 0)
3351        {
3352          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3353             (int) ping_interlace_method);
3354        }
3355      else if (ping_interlace_method == 1)
3356        {
3357          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3358             (int) ping_interlace_method);
3359        }
3360      else
3361        {
3362          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3363             (int) ping_interlace_method);
3364        }
3365        (void) SetImageProperty(image,"png:IHDR.interlace_method",
3366          msg,exception);
3367 
3368      if (number_colors != 0)
3369        {
3370          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3371             (int) number_colors);
3372          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3373             exception);
3374        }
3375    }
3376 #if defined(PNG_tIME_SUPPORTED)
3377    read_tIME_chunk(image,ping,ping_info,exception);
3378 #endif
3379 #if defined(PNG_READ_eXIf_SUPPORTED)
3380   read_eXIf_chunk(image,ping,ping_info,exception);
3381 #endif
3382 
3383 
3384   /*
3385     Read image scanlines.
3386   */
3387   if (image->delay != 0)
3388     mng_info->scenes_found++;
3389 
3390   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3391       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3392       (image_info->first_scene+image_info->number_scenes))))
3393     {
3394       /* This happens later in non-ping decodes */
3395       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3396         image->storage_class=DirectClass;
3397       image->alpha_trait=
3398         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3399          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3400          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3401         BlendPixelTrait : UndefinedPixelTrait;
3402 
3403       if (logging != MagickFalse)
3404         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3405           "    Skipping PNG image data for scene %.20g",(double)
3406           mng_info->scenes_found-1);
3407       png_destroy_read_struct(&ping,&ping_info,&end_info);
3408 
3409 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3410       UnlockSemaphoreInfo(ping_semaphore);
3411 #endif
3412 
3413       if (logging != MagickFalse)
3414         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3415           "  exit ReadOnePNGImage().");
3416 
3417       return(image);
3418     }
3419 
3420   if (logging != MagickFalse)
3421     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3422       "    Reading PNG IDAT chunk(s)");
3423 
3424   status=SetImageExtent(image,image->columns,image->rows,exception);
3425   if (status != MagickFalse)
3426     status=ResetImagePixels(image,exception);
3427   if (status == MagickFalse)
3428     {
3429       png_destroy_read_struct(&ping,&ping_info,&end_info);
3430 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3431       UnlockSemaphoreInfo(ping_semaphore);
3432 #endif
3433       return(DestroyImageList(image));
3434     }
3435 
3436   if (num_passes > 1)
3437     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3438       sizeof(*ping_pixels));
3439   else
3440     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3441 
3442   if (pixel_info == (MemoryInfo *) NULL)
3443     png_error(ping,"Memory allocation failed");
3444   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3445 
3446   if (logging != MagickFalse)
3447     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3448       "    Converting PNG pixels to pixel packets");
3449   /*
3450     Convert PNG pixels to pixel packets.
3451   */
3452   quantum_info=AcquireQuantumInfo(image_info,image);
3453 
3454   if (quantum_info == (QuantumInfo *) NULL)
3455      png_error(ping,"Failed to allocate quantum_info");
3456 
3457   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3458 
3459   {
3460 
3461    MagickBooleanType
3462      found_transparent_pixel;
3463 
3464   found_transparent_pixel=MagickFalse;
3465 
3466   if (image->storage_class == DirectClass)
3467     {
3468       for (pass=0; pass < num_passes; pass++)
3469       {
3470         /*
3471           Convert image to DirectClass pixel packets.
3472         */
3473         image->alpha_trait=
3474             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3475             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3476             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3477             BlendPixelTrait : UndefinedPixelTrait;
3478 
3479         for (y=0; y < (ssize_t) image->rows; y++)
3480         {
3481           if (num_passes > 1)
3482             row_offset=ping_rowbytes*y;
3483 
3484           else
3485             row_offset=0;
3486 
3487           png_read_row(ping,ping_pixels+row_offset,NULL);
3488 
3489           if (pass < num_passes-1)
3490             continue;
3491 
3492           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3493 
3494           if (q == (Quantum *) NULL)
3495             break;
3496 
3497           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3498             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3499               GrayQuantum,ping_pixels+row_offset,exception);
3500 
3501           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3502             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3503               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3504 
3505           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3506             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3507               RGBAQuantum,ping_pixels+row_offset,exception);
3508 
3509           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3510             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3511               IndexQuantum,ping_pixels+row_offset,exception);
3512 
3513           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3514             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3515               RGBQuantum,ping_pixels+row_offset,exception);
3516 
3517           if (found_transparent_pixel == MagickFalse)
3518             {
3519               /* Is there a transparent pixel in the row? */
3520               if (y== 0 && logging != MagickFalse)
3521                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3522                    "    Looking for cheap transparent pixel");
3523 
3524               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3525               {
3526                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3527                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3528                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3529                   {
3530                     if (logging != MagickFalse)
3531                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3532                         "    ...got one.");
3533 
3534                     found_transparent_pixel = MagickTrue;
3535                     break;
3536                   }
3537                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3538                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3539                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3540                     transparent_color.red &&
3541                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3542                     transparent_color.green &&
3543                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3544                     transparent_color.blue))
3545                   {
3546                     if (logging != MagickFalse)
3547                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3548                         "    ...got one.");
3549                     found_transparent_pixel = MagickTrue;
3550                     break;
3551                   }
3552                 q+=GetPixelChannels(image);
3553               }
3554             }
3555 
3556           if (num_passes == 1)
3557             {
3558               status=SetImageProgress(image,LoadImageTag,
3559                   (MagickOffsetType) y, image->rows);
3560 
3561               if (status == MagickFalse)
3562                 break;
3563             }
3564           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3565             break;
3566         }
3567         if (y < (long) image->rows)
3568           break;
3569 
3570         if (num_passes != 1)
3571           {
3572             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3573             if (status == MagickFalse)
3574               break;
3575           }
3576       }
3577     }
3578 
3579   else /* image->storage_class != DirectClass */
3580 
3581     for (pass=0; pass < num_passes; pass++)
3582     {
3583       register Quantum
3584         *r;
3585 
3586       /*
3587         Convert grayscale image to PseudoClass pixel packets.
3588       */
3589       if (logging != MagickFalse)
3590         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3591           "    Converting grayscale pixels to pixel packets");
3592 
3593       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3594         BlendPixelTrait : UndefinedPixelTrait;
3595 
3596       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3597         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3598         sizeof(*quantum_scanline));
3599 
3600       if (quantum_scanline == (Quantum *) NULL)
3601         png_error(ping,"Memory allocation failed");
3602 
3603       for (y=0; y < (ssize_t) image->rows; y++)
3604       {
3605         Quantum
3606            alpha;
3607 
3608         if (num_passes > 1)
3609           row_offset=ping_rowbytes*y;
3610 
3611         else
3612           row_offset=0;
3613 
3614         png_read_row(ping,ping_pixels+row_offset,NULL);
3615 
3616         if (pass < num_passes-1)
3617           continue;
3618 
3619         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3620 
3621         if (q == (Quantum *) NULL)
3622           break;
3623 
3624         p=ping_pixels+row_offset;
3625         r=quantum_scanline;
3626 
3627         switch (ping_bit_depth)
3628         {
3629           case 8:
3630           {
3631 
3632             if (ping_color_type == 4)
3633               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3634               {
3635                 *r++=*p++;
3636 
3637                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3638 
3639                 SetPixelAlpha(image,alpha,q);
3640 
3641                 if (alpha != OpaqueAlpha)
3642                   found_transparent_pixel = MagickTrue;
3643 
3644                 q+=GetPixelChannels(image);
3645               }
3646 
3647             else
3648               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3649                 *r++=*p++;
3650 
3651             break;
3652           }
3653 
3654           case 16:
3655           {
3656             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3657             {
3658 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3659               unsigned long
3660                 quantum;
3661 
3662               if (image->colors > 256)
3663                 quantum=(((unsigned int) *p++) << 8);
3664 
3665               else
3666                 quantum=0;
3667 
3668               quantum|=(*p++);
3669               *r=ScaleShortToQuantum(quantum);
3670               r++;
3671 
3672               if (ping_color_type == 4)
3673                 {
3674                   if (image->colors > 256)
3675                     quantum=(((unsigned int) *p++) << 8);
3676                   else
3677                     quantum=0;
3678 
3679                   quantum|=(*p++);
3680 
3681                   alpha=ScaleShortToQuantum(quantum);
3682                   SetPixelAlpha(image,alpha,q);
3683 
3684                   if (alpha != OpaqueAlpha)
3685                     found_transparent_pixel = MagickTrue;
3686 
3687                   q+=GetPixelChannels(image);
3688                 }
3689 
3690 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3691               *r++=(*p++);
3692               p++; /* strip low byte */
3693 
3694               if (ping_color_type == 4)
3695                 {
3696                   SetPixelAlpha(image,*p++,q);
3697 
3698                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3699                     found_transparent_pixel = MagickTrue;
3700 
3701                   p++;
3702                   q+=GetPixelChannels(image);
3703                 }
3704 #endif
3705             }
3706 
3707             break;
3708           }
3709 
3710           default:
3711             break;
3712         }
3713 
3714         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3715           break;
3716 
3717         /*
3718           Transfer image scanline.
3719         */
3720         r=quantum_scanline;
3721 
3722         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3723 
3724         if (q == (Quantum *) NULL)
3725           break;
3726         for (x=0; x < (ssize_t) image->columns; x++)
3727         {
3728           ssize_t index=ConstrainColormapIndex(image,(ssize_t) *r,exception);
3729           SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
3730           SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
3731           SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
3732           SetPixelIndex(image,index,q);
3733           r++;
3734           q+=GetPixelChannels(image);
3735         }
3736 
3737         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3738           break;
3739 
3740         if (num_passes == 1)
3741           {
3742             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3743               image->rows);
3744 
3745             if (status == MagickFalse)
3746               break;
3747           }
3748       }
3749       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3750       if (y < (long) image->rows)
3751         break;
3752       if (num_passes != 1)
3753         {
3754           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3755 
3756           if (status == MagickFalse)
3757             break;
3758         }
3759     }
3760 
3761     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3762       UndefinedPixelTrait;
3763 
3764     if (logging != MagickFalse)
3765       {
3766         if (found_transparent_pixel != MagickFalse)
3767           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3768             "    Found transparent pixel");
3769         else
3770           {
3771             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3772               "    No transparent pixel was found");
3773 
3774             ping_color_type&=0x03;
3775           }
3776       }
3777   }
3778   quantum_info=DestroyQuantumInfo(quantum_info);
3779 
3780   png_read_end(ping,end_info);
3781 
3782   if (logging != MagickFalse)
3783   {
3784     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3785        "  image->storage_class=%d\n",(int) image->storage_class);
3786   }
3787 
3788   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3789       (ssize_t) image_info->first_scene && image->delay != 0)
3790     {
3791       png_destroy_read_struct(&ping,&ping_info,&end_info);
3792       pixel_info=RelinquishVirtualMemory(pixel_info);
3793       image->colors=2;
3794       (void) SetImageBackgroundColor(image,exception);
3795 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3796       UnlockSemaphoreInfo(ping_semaphore);
3797 #endif
3798       if (logging != MagickFalse)
3799         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3800           "  exit ReadOnePNGImage() early.");
3801       return(image);
3802     }
3803 
3804   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3805     {
3806       ClassType
3807         storage_class;
3808 
3809       /*
3810         Image has a transparent background.
3811       */
3812       storage_class=image->storage_class;
3813       image->alpha_trait=BlendPixelTrait;
3814 
3815 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3816 
3817       if (storage_class == PseudoClass)
3818         {
3819           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3820             {
3821               for (x=0; x < ping_num_trans; x++)
3822               {
3823                  image->colormap[x].alpha_trait=BlendPixelTrait;
3824                  image->colormap[x].alpha =
3825                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3826               }
3827             }
3828 
3829           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3830             {
3831               for (x=0; x < (int) image->colors; x++)
3832               {
3833                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3834                      transparent_color.alpha)
3835                  {
3836                     image->colormap[x].alpha_trait=BlendPixelTrait;
3837                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3838                  }
3839               }
3840             }
3841           (void) SyncImage(image,exception);
3842         }
3843 
3844 #if 1 /* Should have already been done above, but glennrp problem P10
3845        * needs this.
3846        */
3847       else
3848         {
3849           for (y=0; y < (ssize_t) image->rows; y++)
3850           {
3851             image->storage_class=storage_class;
3852             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3853 
3854             if (q == (Quantum *) NULL)
3855               break;
3856 
3857 
3858             /* Caution: on a Q8 build, this does not distinguish between
3859              * 16-bit colors that differ only in the low byte
3860              */
3861             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3862             {
3863               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3864                   transparent_color.red &&
3865                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3866                   transparent_color.green &&
3867                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3868                   transparent_color.blue)
3869                 {
3870                   SetPixelAlpha(image,TransparentAlpha,q);
3871                 }
3872               else
3873                 {
3874                   SetPixelAlpha(image,OpaqueAlpha,q);
3875                 }
3876 
3877               q+=GetPixelChannels(image);
3878             }
3879 
3880             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3881                break;
3882           }
3883         }
3884 #endif
3885 
3886       image->storage_class=DirectClass;
3887     }
3888 
3889   for (j = 0; j < 2; j++)
3890   {
3891     if (j == 0)
3892       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3893           MagickTrue : MagickFalse;
3894     else
3895       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3896           MagickTrue : MagickFalse;
3897 
3898     if (status != MagickFalse)
3899       for (i=0; i < (ssize_t) num_text; i++)
3900       {
3901         /* Check for a profile */
3902 
3903         if (logging != MagickFalse)
3904           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3905             "    Reading PNG text chunk");
3906 
3907         if (strlen(text[i].key) > 16 &&
3908             memcmp(text[i].key, "Raw profile type ",17) == 0)
3909           {
3910             const char
3911               *value;
3912 
3913             value=GetImageOption(image_info,"profile:skip");
3914 
3915             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3916             {
3917                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3918                   (int) i,exception);
3919                num_raw_profiles++;
3920                if (logging != MagickFalse)
3921                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3922                    "    Read raw profile %s",text[i].key+17);
3923             }
3924             else
3925             {
3926                if (logging != MagickFalse)
3927                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3928                    "    Skipping raw profile %s",text[i].key+17);
3929             }
3930           }
3931 
3932         else
3933           {
3934             char
3935               *value;
3936 
3937             length=text[i].text_length;
3938             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3939               sizeof(*value));
3940             if (value == (char *) NULL)
3941               {
3942                 png_error(ping,"Memory allocation failed");
3943                 break;
3944               }
3945             *value='\0';
3946             (void) ConcatenateMagickString(value,text[i].text,length+2);
3947 
3948             /* Don't save "density" or "units" property if we have a pHYs
3949              * chunk
3950              */
3951             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3952                 (LocaleCompare(text[i].key,"density") != 0 &&
3953                 LocaleCompare(text[i].key,"units") != 0))
3954               {
3955                 char
3956                   key[MagickPathExtent];
3957 
3958                 (void) FormatLocaleString(key,MagickPathExtent,"%s",
3959                   text[i].key);
3960                 if (LocaleCompare(key,"version") == 0)
3961                   (void) FormatLocaleString(key,MagickPathExtent,"png:%s",
3962                     text[i].key);
3963                 (void) SetImageProperty(image,key,value,exception);
3964               }
3965 
3966             if (logging != MagickFalse)
3967             {
3968               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3969                 "      length: %lu\n"
3970                 "      Keyword: %s",
3971                 (unsigned long) length,
3972                 text[i].key);
3973             }
3974 
3975             value=DestroyString(value);
3976           }
3977       }
3978     num_text_total += num_text;
3979   }
3980 
3981 #ifdef MNG_OBJECT_BUFFERS
3982   /*
3983     Store the object if necessary.
3984   */
3985   if (object_id && !mng_info->frozen[object_id])
3986     {
3987       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3988         {
3989           /*
3990             create a new object buffer.
3991           */
3992           mng_info->ob[object_id]=(MngBuffer *)
3993             AcquireMagickMemory(sizeof(MngBuffer));
3994 
3995           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3996             {
3997               mng_info->ob[object_id]->image=(Image *) NULL;
3998               mng_info->ob[object_id]->reference_count=1;
3999             }
4000         }
4001 
4002       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
4003           mng_info->ob[object_id]->frozen)
4004         {
4005           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4006              png_error(ping,"Memory allocation failed");
4007 
4008           if (mng_info->ob[object_id]->frozen)
4009             png_error(ping,"Cannot overwrite frozen MNG object buffer");
4010         }
4011 
4012       else
4013         {
4014 
4015           if (mng_info->ob[object_id]->image != (Image *) NULL)
4016             mng_info->ob[object_id]->image=DestroyImage
4017                 (mng_info->ob[object_id]->image);
4018 
4019           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4020             exception);
4021 
4022           if (mng_info->ob[object_id]->image != (Image *) NULL)
4023             mng_info->ob[object_id]->image->file=(FILE *) NULL;
4024 
4025           else
4026             png_error(ping, "Cloning image for object buffer failed");
4027 
4028           if (ping_width > 250000L || ping_height > 250000L)
4029              png_error(ping,"PNG Image dimensions are too large.");
4030 
4031           mng_info->ob[object_id]->width=ping_width;
4032           mng_info->ob[object_id]->height=ping_height;
4033           mng_info->ob[object_id]->color_type=ping_color_type;
4034           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4035           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4036           mng_info->ob[object_id]->compression_method=
4037              ping_compression_method;
4038           mng_info->ob[object_id]->filter_method=ping_filter_method;
4039 
4040           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4041             {
4042               png_colorp
4043                 plte;
4044 
4045               /*
4046                 Copy the PLTE to the object buffer.
4047               */
4048               png_get_PLTE(ping,ping_info,&plte,&number_colors);
4049               mng_info->ob[object_id]->plte_length=number_colors;
4050 
4051               for (i=0; i < number_colors; i++)
4052               {
4053                 mng_info->ob[object_id]->plte[i]=plte[i];
4054               }
4055             }
4056 
4057           else
4058               mng_info->ob[object_id]->plte_length=0;
4059         }
4060     }
4061 #endif
4062 
4063    /* Set image->alpha_trait to MagickTrue if the input colortype supports
4064     * alpha or if a valid tRNS chunk is present, no matter whether there
4065     * is actual transparency present.
4066     */
4067     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4068         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4069         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4070         BlendPixelTrait : UndefinedPixelTrait;
4071     if (image->alpha_trait == BlendPixelTrait)
4072       (void) SetImageStorageClass(image,DirectClass,exception);
4073 
4074 #if 0  /* I'm not sure what's wrong here but it does not work. */
4075     if (image->alpha_trait != UndefinedPixelTrait)
4076     {
4077       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4078         (void) SetImageType(image,GrayscaleAlphaType,exception);
4079 
4080       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4081         (void) SetImageType(image,PaletteAlphaType,exception);
4082 
4083       else
4084         (void) SetImageType(image,TrueColorAlphaType,exception);
4085     }
4086 
4087     else
4088     {
4089       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4090         (void) SetImageType(image,GrayscaleType,exception);
4091 
4092       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4093         (void) SetImageType(image,PaletteType,exception);
4094 
4095       else
4096         (void) SetImageType(image,TrueColorType,exception);
4097     }
4098 #endif
4099 
4100    /* Set more properties for identify to retrieve */
4101    {
4102      char
4103        msg[MagickPathExtent];
4104 
4105      if (num_text_total != 0)
4106        {
4107          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4108          (void) FormatLocaleString(msg,MagickPathExtent,
4109             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4110          (void) SetImageProperty(image,"png:text",msg,
4111                 exception);
4112        }
4113 
4114      if (num_raw_profiles != 0)
4115        {
4116          (void) FormatLocaleString(msg,MagickPathExtent,
4117             "%d were found", num_raw_profiles);
4118          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4119                 exception);
4120        }
4121 
4122      /* cHRM chunk: */
4123      if (ping_found_cHRM != MagickFalse)
4124        {
4125          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4126             "chunk was found (see Chromaticity, above)");
4127          (void) SetImageProperty(image,"png:cHRM",msg,
4128                 exception);
4129        }
4130 
4131      /* bKGD chunk: */
4132      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4133        {
4134          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4135             "chunk was found (see Background color, above)");
4136          (void) SetImageProperty(image,"png:bKGD",msg,
4137                 exception);
4138        }
4139 
4140      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4141         "chunk was found");
4142 
4143 #if defined(PNG_iCCP_SUPPORTED)
4144      /* iCCP chunk: */
4145      if (ping_found_iCCP != MagickFalse)
4146         (void) SetImageProperty(image,"png:iCCP",msg,
4147                 exception);
4148 #endif
4149 
4150      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4151         (void) SetImageProperty(image,"png:tRNS",msg,
4152                 exception);
4153 
4154 #if defined(PNG_sRGB_SUPPORTED)
4155      /* sRGB chunk: */
4156      if (ping_found_sRGB != MagickFalse)
4157        {
4158          (void) FormatLocaleString(msg,MagickPathExtent,
4159             "intent=%d (%s)",
4160             (int) intent,
4161             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4162          (void) SetImageProperty(image,"png:sRGB",msg,
4163                  exception);
4164        }
4165 #endif
4166 
4167      /* gAMA chunk: */
4168      if (ping_found_gAMA != MagickFalse)
4169        {
4170          (void) FormatLocaleString(msg,MagickPathExtent,
4171             "gamma=%.8g (See Gamma, above)",
4172             file_gamma);
4173          (void) SetImageProperty(image,"png:gAMA",msg,
4174                 exception);
4175        }
4176 
4177 #if defined(PNG_pHYs_SUPPORTED)
4178      /* pHYs chunk: */
4179      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4180        {
4181          (void) FormatLocaleString(msg,MagickPathExtent,
4182             "x_res=%.10g, y_res=%.10g, units=%d",
4183             (double) x_resolution,(double) y_resolution, unit_type);
4184          (void) SetImageProperty(image,"png:pHYs",msg,
4185                 exception);
4186        }
4187 #endif
4188 
4189 #if defined(PNG_oFFs_SUPPORTED)
4190      /* oFFs chunk: */
4191      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4192        {
4193          (void) FormatLocaleString(msg,MagickPathExtent,
4194             "x_off=%.20g, y_off=%.20g",
4195             (double) image->page.x,(double) image->page.y);
4196          (void) SetImageProperty(image,"png:oFFs",msg,
4197                 exception);
4198        }
4199 #endif
4200 
4201 #if defined(PNG_tIME_SUPPORTED)
4202      read_tIME_chunk(image,ping,end_info,exception);
4203 #endif
4204 #if defined(PNG_READ_eXIf_SUPPORTED)
4205     read_eXIf_chunk(image,ping,end_info,exception);
4206 #endif
4207 
4208      /* caNv chunk: */
4209      if ((image->page.width != 0 && image->page.width != image->columns) ||
4210          (image->page.height != 0 && image->page.height != image->rows) ||
4211          (image->page.x != 0 || image->page.y != 0))
4212        {
4213          (void) FormatLocaleString(msg,MagickPathExtent,
4214             "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4215             (double) image->page.width,(double) image->page.height,
4216             (double) image->page.x,(double) image->page.y);
4217          (void) SetImageProperty(image,"png:caNv",msg,
4218                 exception);
4219        }
4220    }
4221 
4222   /*
4223     Relinquish resources.
4224   */
4225   png_destroy_read_struct(&ping,&ping_info,&end_info);
4226 
4227   pixel_info=RelinquishVirtualMemory(pixel_info);
4228 
4229   if (logging != MagickFalse)
4230     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4231       "  exit ReadOnePNGImage()");
4232 
4233 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4234   UnlockSemaphoreInfo(ping_semaphore);
4235 #endif
4236 
4237   /* }  for navigation to beginning of SETJMP-protected block, revert to
4238    *    Throwing an Exception when an error occurs.
4239    */
4240 
4241   return(image);
4242 
4243 /* end of reading one PNG image */
4244 }
4245 
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)4246 static Image *ReadPNGImage(const ImageInfo *image_info,
4247   ExceptionInfo *exception)
4248 {
4249   Image
4250     *image;
4251 
4252   MagickBooleanType
4253     logging,
4254     status;
4255 
4256   MngInfo
4257     *mng_info;
4258 
4259   char
4260     magic_number[MagickPathExtent];
4261 
4262   ssize_t
4263     count;
4264 
4265   /*
4266     Open image file.
4267   */
4268   assert(image_info != (const ImageInfo *) NULL);
4269   assert(image_info->signature == MagickCoreSignature);
4270 
4271   if (image_info->debug != MagickFalse)
4272     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4273       image_info->filename);
4274 
4275   assert(exception != (ExceptionInfo *) NULL);
4276   assert(exception->signature == MagickCoreSignature);
4277   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4278   image=AcquireImage(image_info,exception);
4279   mng_info=(MngInfo *) NULL;
4280   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4281 
4282   if (status == MagickFalse)
4283     return(DestroyImageList(image));
4284 
4285   /*
4286     Verify PNG signature.
4287   */
4288   count=ReadBlob(image,8,(unsigned char *) magic_number);
4289 
4290   if ((count < 8) || (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0))
4291     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4292 
4293   /*
4294      Verify that file size large enough to contain a PNG datastream.
4295   */
4296   if (GetBlobSize(image) < 61)
4297     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4298 
4299   /*
4300     Allocate a MngInfo structure.
4301   */
4302   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4303 
4304   if (mng_info == (MngInfo *) NULL)
4305     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4306 
4307   /*
4308     Initialize members of the MngInfo structure.
4309   */
4310   (void) memset(mng_info,0,sizeof(MngInfo));
4311   mng_info->image=image;
4312 
4313   image=ReadOnePNGImage(mng_info,image_info,exception);
4314   mng_info=MngInfoFreeStruct(mng_info);
4315 
4316   if (image == (Image *) NULL)
4317     {
4318       if (logging != MagickFalse)
4319         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4320           "exit ReadPNGImage() with error");
4321 
4322       return((Image *) NULL);
4323     }
4324 
4325   (void) CloseBlob(image);
4326 
4327   if ((image->columns == 0) || (image->rows == 0))
4328     {
4329       if (logging != MagickFalse)
4330         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4331           "exit ReadPNGImage() with error.");
4332 
4333       ThrowReaderException(CorruptImageError,"CorruptImage");
4334     }
4335 
4336   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4337       ((image->gamma < .45) || (image->gamma > .46)) &&
4338            !(image->chromaticity.red_primary.x>0.6399f &&
4339            image->chromaticity.red_primary.x<0.6401f &&
4340            image->chromaticity.red_primary.y>0.3299f &&
4341            image->chromaticity.red_primary.y<0.3301f &&
4342            image->chromaticity.green_primary.x>0.2999f &&
4343            image->chromaticity.green_primary.x<0.3001f &&
4344            image->chromaticity.green_primary.y>0.5999f &&
4345            image->chromaticity.green_primary.y<0.6001f &&
4346            image->chromaticity.blue_primary.x>0.1499f &&
4347            image->chromaticity.blue_primary.x<0.1501f &&
4348            image->chromaticity.blue_primary.y>0.0599f &&
4349            image->chromaticity.blue_primary.y<0.0601f &&
4350            image->chromaticity.white_point.x>0.3126f &&
4351            image->chromaticity.white_point.x<0.3128f &&
4352            image->chromaticity.white_point.y>0.3289f &&
4353            image->chromaticity.white_point.y<0.3291f))
4354     {
4355        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4356           "SetImageColorspace to RGBColorspace");
4357        SetImageColorspace(image,RGBColorspace,exception);
4358     }
4359 
4360   if (logging != MagickFalse)
4361     {
4362        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4363            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4364                (double) image->page.width,(double) image->page.height,
4365                (double) image->page.x,(double) image->page.y);
4366        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4367            "  image->colorspace: %d", (int) image->colorspace);
4368     }
4369 
4370   if (logging != MagickFalse)
4371     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4372 
4373   return(image);
4374 }
4375 
4376 
4377 
4378 #if defined(JNG_SUPPORTED)
4379 /*
4380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4381 %                                                                             %
4382 %                                                                             %
4383 %                                                                             %
4384 %   R e a d O n e J N G I m a g e                                             %
4385 %                                                                             %
4386 %                                                                             %
4387 %                                                                             %
4388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4389 %
4390 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4391 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4392 %  necessary for the new Image structure and returns a pointer to the new
4393 %  image.
4394 %
4395 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4396 %
4397 %  The format of the ReadOneJNGImage method is:
4398 %
4399 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4400 %         ExceptionInfo *exception)
4401 %
4402 %  A description of each parameter follows:
4403 %
4404 %    o mng_info: Specifies a pointer to a MngInfo structure.
4405 %
4406 %    o image_info: the image info.
4407 %
4408 %    o exception: return any errors or warnings in this structure.
4409 %
4410 */
4411 static void
DestroyJNG(unsigned char * chunk,Image ** color_image,ImageInfo ** color_image_info,Image ** alpha_image,ImageInfo ** alpha_image_info)4412 DestroyJNG(unsigned char *chunk,Image **color_image,
4413   ImageInfo **color_image_info,Image **alpha_image,
4414   ImageInfo **alpha_image_info)
4415 {
4416   (void) RelinquishMagickMemory(chunk);
4417   if (color_image_info && *color_image_info)
4418   {
4419     DestroyImageInfo(*color_image_info);
4420     *color_image_info = (ImageInfo *)NULL;
4421   }
4422   if (alpha_image_info && *alpha_image_info)
4423   {
4424     DestroyImageInfo(*alpha_image_info);
4425     *alpha_image_info = (ImageInfo *)NULL;
4426   }
4427   if (color_image && *color_image)
4428   {
4429     DestroyImage(*color_image);
4430     *color_image = (Image *)NULL;
4431   }
4432   if (alpha_image && *alpha_image)
4433   {
4434     DestroyImage(*alpha_image);
4435     *alpha_image = (Image *)NULL;
4436   }
4437 }
ReadOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)4438 static Image *ReadOneJNGImage(MngInfo *mng_info,
4439     const ImageInfo *image_info, ExceptionInfo *exception)
4440 {
4441   Image
4442     *alpha_image,
4443     *color_image,
4444     *image,
4445     *jng_image;
4446 
4447   ImageInfo
4448     *alpha_image_info,
4449     *color_image_info;
4450 
4451   MagickBooleanType
4452     logging;
4453 
4454   ssize_t
4455     y;
4456 
4457   MagickBooleanType
4458     status;
4459 
4460   png_uint_32
4461     jng_height,
4462     jng_width;
4463 
4464   png_byte
4465     jng_color_type,
4466     jng_image_sample_depth,
4467     jng_image_compression_method,
4468     jng_image_interlace_method,
4469     jng_alpha_sample_depth,
4470     jng_alpha_compression_method,
4471     jng_alpha_filter_method,
4472     jng_alpha_interlace_method;
4473 
4474   register const Quantum
4475     *s;
4476 
4477   register ssize_t
4478     i,
4479     x;
4480 
4481   register Quantum
4482     *q;
4483 
4484   register unsigned char
4485     *p;
4486 
4487   unsigned int
4488     read_JSEP,
4489     reading_idat;
4490 
4491   size_t
4492     length;
4493 
4494   jng_alpha_compression_method=0;
4495   jng_alpha_sample_depth=8;
4496   jng_color_type=0;
4497   jng_height=0;
4498   jng_width=0;
4499   alpha_image=(Image *) NULL;
4500   color_image=(Image *) NULL;
4501   alpha_image_info=(ImageInfo *) NULL;
4502   color_image_info=(ImageInfo *) NULL;
4503 
4504   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4505     "  Enter ReadOneJNGImage()");
4506 
4507   image=mng_info->image;
4508 
4509   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4510     {
4511       /*
4512         Allocate next image structure.
4513       */
4514       if (logging != MagickFalse)
4515         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4516            "  AcquireNextImage()");
4517 
4518       AcquireNextImage(image_info,image,exception);
4519 
4520       if (GetNextImageInList(image) == (Image *) NULL)
4521         return(DestroyImageList(image));
4522 
4523       image=SyncNextImageInList(image);
4524     }
4525   mng_info->image=image;
4526 
4527   /*
4528     Signature bytes have already been read.
4529   */
4530 
4531   read_JSEP=MagickFalse;
4532   reading_idat=MagickFalse;
4533   for (;;)
4534   {
4535     char
4536       type[MagickPathExtent];
4537 
4538     unsigned char
4539       *chunk;
4540 
4541     unsigned int
4542       count;
4543 
4544     /*
4545       Read a new JNG chunk.
4546     */
4547     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4548       2*GetBlobSize(image));
4549 
4550     if (status == MagickFalse)
4551       break;
4552 
4553     type[0]='\0';
4554     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4555     length=(size_t) ReadBlobMSBLong(image);
4556     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4557 
4558     if (logging != MagickFalse)
4559       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4560         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4561         type[0],type[1],type[2],type[3],(double) length);
4562 
4563     if (length > PNG_UINT_31_MAX || count == 0)
4564       {
4565         DestroyJNG(NULL,&color_image,&color_image_info,
4566           &alpha_image,&alpha_image_info);
4567         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4568       }
4569     if (length > GetBlobSize(image))
4570       {
4571         DestroyJNG(NULL,&color_image,&color_image_info,
4572           &alpha_image,&alpha_image_info);
4573         ThrowReaderException(CorruptImageError,
4574           "InsufficientImageDataInFile");
4575       }
4576 
4577     p=NULL;
4578     chunk=(unsigned char *) NULL;
4579 
4580     if (length != 0)
4581       {
4582         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4583 
4584         if (chunk == (unsigned char *) NULL)
4585           {
4586             DestroyJNG(NULL,&color_image,&color_image_info,
4587               &alpha_image,&alpha_image_info);
4588             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4589           }
4590 
4591         for (i=0; i < (ssize_t) length; i++)
4592         {
4593           int
4594             c;
4595 
4596           c=ReadBlobByte(image);
4597           if (c == EOF)
4598             break;
4599           chunk[i]=(unsigned char) c;
4600         }
4601         for ( ; i < (ssize_t) length; i++)
4602           chunk[i]='\0';
4603 
4604         p=chunk;
4605       }
4606 
4607     (void) ReadBlobMSBLong(image);  /* read crc word */
4608 
4609     if (memcmp(type,mng_JHDR,4) == 0)
4610       {
4611         if (length == 16)
4612           {
4613             jng_width=(png_uint_32)mng_get_long(p);
4614             jng_height=(png_uint_32)mng_get_long(&p[4]);
4615             if ((jng_width == 0) || (jng_height == 0))
4616               {
4617                 DestroyJNG(chunk,&color_image,&color_image_info,
4618                   &alpha_image,&alpha_image_info);
4619                 ThrowReaderException(CorruptImageError,
4620                   "NegativeOrZeroImageSize");
4621               }
4622             jng_color_type=p[8];
4623             jng_image_sample_depth=p[9];
4624             jng_image_compression_method=p[10];
4625             jng_image_interlace_method=p[11];
4626 
4627             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4628               NoInterlace;
4629 
4630             jng_alpha_sample_depth=p[12];
4631             jng_alpha_compression_method=p[13];
4632             jng_alpha_filter_method=p[14];
4633             jng_alpha_interlace_method=p[15];
4634 
4635             if (logging != MagickFalse)
4636               {
4637                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4638                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4639                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4640                   "    jng_image_compression_method:%3d",
4641                   (unsigned long) jng_width, (unsigned long) jng_height,
4642                   jng_color_type, jng_image_sample_depth,
4643                   jng_image_compression_method);
4644 
4645                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4646                   "    jng_image_interlace_method:  %3d"
4647                   "    jng_alpha_sample_depth:      %3d",
4648                   jng_image_interlace_method,
4649                   jng_alpha_sample_depth);
4650 
4651                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4652                   "    jng_alpha_compression_method:%3d\n"
4653                   "    jng_alpha_filter_method:     %3d\n"
4654                   "    jng_alpha_interlace_method:  %3d",
4655                   jng_alpha_compression_method,
4656                   jng_alpha_filter_method,
4657                   jng_alpha_interlace_method);
4658               }
4659           }
4660 
4661         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4662 
4663         if ((jng_width > 65535) || (jng_height > 65535) ||
4664             (MagickSizeType) jng_width > GetMagickResourceLimit(WidthResource) ||
4665             (MagickSizeType) jng_height > GetMagickResourceLimit(HeightResource))
4666           {
4667             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4668                "    JNG width or height too large: (%lu x %lu)",
4669                 (long) jng_width, (long) jng_height);
4670             DestroyJNG(chunk,&color_image,&color_image_info,
4671               &alpha_image,&alpha_image_info);
4672             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4673           }
4674 
4675         continue;
4676       }
4677 
4678 
4679     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4680         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4681          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4682       {
4683         /*
4684            o create color_image
4685            o open color_blob, attached to color_image
4686            o if (color type has alpha)
4687                open alpha_blob, attached to alpha_image
4688         */
4689 
4690         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4691 
4692         if (color_image_info == (ImageInfo *) NULL)
4693         {
4694           DestroyJNG(chunk,&color_image,&color_image_info,
4695               &alpha_image,&alpha_image_info);
4696           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4697         }
4698 
4699         GetImageInfo(color_image_info);
4700         color_image=AcquireImage(color_image_info,exception);
4701 
4702         if (color_image == (Image *) NULL)
4703         {
4704           DestroyJNG(chunk,&color_image,&color_image_info,
4705               &alpha_image,&alpha_image_info);
4706           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4707         }
4708 
4709         if (logging != MagickFalse)
4710           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4711             "    Creating color_blob.");
4712 
4713         (void) AcquireUniqueFilename(color_image->filename);
4714         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4715           exception);
4716 
4717         if (status == MagickFalse)
4718           {
4719             DestroyJNG(chunk,&color_image,&color_image_info,
4720               &alpha_image,&alpha_image_info);
4721             return(DestroyImageList(image));
4722           }
4723 
4724         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4725           {
4726             alpha_image_info=(ImageInfo *)
4727               AcquireMagickMemory(sizeof(ImageInfo));
4728 
4729             if (alpha_image_info == (ImageInfo *) NULL)
4730               {
4731                 DestroyJNG(chunk,&color_image,&color_image_info,
4732                   &alpha_image,&alpha_image_info);
4733                 ThrowReaderException(ResourceLimitError,
4734                   "MemoryAllocationFailed");
4735               }
4736 
4737             GetImageInfo(alpha_image_info);
4738             alpha_image=AcquireImage(alpha_image_info,exception);
4739 
4740             if (alpha_image == (Image *) NULL)
4741               {
4742                 DestroyJNG(chunk,&color_image,&color_image_info,
4743                   &alpha_image,&alpha_image_info);
4744                 ThrowReaderException(ResourceLimitError,
4745                   "MemoryAllocationFailed");
4746               }
4747 
4748             if (logging != MagickFalse)
4749               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4750                 "    Creating alpha_blob.");
4751 
4752             (void) AcquireUniqueFilename(alpha_image->filename);
4753             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4754               exception);
4755 
4756             if (status == MagickFalse)
4757               {
4758                 DestroyJNG(chunk,&color_image,&color_image_info,
4759                   &alpha_image,&alpha_image_info);
4760                 return(DestroyImageList(image));
4761               }
4762 
4763             if (jng_alpha_compression_method == 0)
4764               {
4765                 unsigned char
4766                   data[18];
4767 
4768                 if (logging != MagickFalse)
4769                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4770                     "    Writing IHDR chunk to alpha_blob.");
4771 
4772                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4773                   "\211PNG\r\n\032\n");
4774 
4775                 (void) WriteBlobMSBULong(alpha_image,13L);
4776                 PNGType(data,mng_IHDR);
4777                 LogPNGChunk(logging,mng_IHDR,13L);
4778                 PNGLong(data+4,jng_width);
4779                 PNGLong(data+8,jng_height);
4780                 data[12]=jng_alpha_sample_depth;
4781                 data[13]=0; /* color_type gray */
4782                 data[14]=0; /* compression method 0 */
4783                 data[15]=0; /* filter_method 0 */
4784                 data[16]=0; /* interlace_method 0 */
4785                 (void) WriteBlob(alpha_image,17,data);
4786                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4787               }
4788           }
4789         reading_idat=MagickTrue;
4790       }
4791 
4792     if (memcmp(type,mng_JDAT,4) == 0)
4793       {
4794         /* Copy chunk to color_image->blob */
4795 
4796         if (logging != MagickFalse)
4797           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4798             "    Copying JDAT chunk data to color_blob.");
4799 
4800         if ((length != 0) && (color_image != (Image *) NULL))
4801           (void) WriteBlob(color_image,length,chunk);
4802         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4803         continue;
4804       }
4805 
4806     if (memcmp(type,mng_IDAT,4) == 0)
4807       {
4808         png_byte
4809            data[5];
4810 
4811         /* Copy IDAT header and chunk data to alpha_image->blob */
4812 
4813         if (alpha_image != NULL && image_info->ping == MagickFalse)
4814           {
4815             if (logging != MagickFalse)
4816               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4817                 "    Copying IDAT chunk data to alpha_blob.");
4818 
4819             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4820             PNGType(data,mng_IDAT);
4821             LogPNGChunk(logging,mng_IDAT,length);
4822             (void) WriteBlob(alpha_image,4,data);
4823             (void) WriteBlob(alpha_image,length,chunk);
4824             (void) WriteBlobMSBULong(alpha_image,
4825               crc32(crc32(0,data,4),chunk,(uInt) length));
4826           }
4827 
4828         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4829 
4830         continue;
4831       }
4832 
4833     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4834       {
4835         /* Copy chunk data to alpha_image->blob */
4836 
4837         if ((alpha_image != NULL) && (image_info->ping == MagickFalse) &&
4838             (length != 0))
4839           {
4840             if (logging != MagickFalse)
4841               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4842                 "    Copying JDAA chunk data to alpha_blob.");
4843 
4844             (void) WriteBlob(alpha_image,length,chunk);
4845           }
4846 
4847         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4848 
4849         continue;
4850       }
4851 
4852     if (memcmp(type,mng_JSEP,4) == 0)
4853       {
4854         read_JSEP=MagickTrue;
4855 
4856         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4857 
4858         continue;
4859       }
4860 
4861     if (memcmp(type,mng_bKGD,4) == 0)
4862       {
4863         if (length == 2)
4864           {
4865             image->background_color.red=ScaleCharToQuantum(p[1]);
4866             image->background_color.green=image->background_color.red;
4867             image->background_color.blue=image->background_color.red;
4868           }
4869 
4870         if (length == 6)
4871           {
4872             image->background_color.red=ScaleCharToQuantum(p[1]);
4873             image->background_color.green=ScaleCharToQuantum(p[3]);
4874             image->background_color.blue=ScaleCharToQuantum(p[5]);
4875           }
4876 
4877         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4878         continue;
4879       }
4880 
4881     if (memcmp(type,mng_gAMA,4) == 0)
4882       {
4883         if (length == 4)
4884           image->gamma=((float) mng_get_long(p))*0.00001;
4885 
4886         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4887         continue;
4888       }
4889 
4890     if (memcmp(type,mng_cHRM,4) == 0)
4891       {
4892         if (length == 32)
4893           {
4894             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4895             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4896             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4897             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4898             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4899             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4900             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4901             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4902           }
4903 
4904         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4905         continue;
4906       }
4907 
4908     if (memcmp(type,mng_sRGB,4) == 0)
4909       {
4910         if (length == 1)
4911           {
4912             image->rendering_intent=
4913               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4914             image->gamma=0.45455f;
4915             image->chromaticity.red_primary.x=0.6400f;
4916             image->chromaticity.red_primary.y=0.3300f;
4917             image->chromaticity.green_primary.x=0.3000f;
4918             image->chromaticity.green_primary.y=0.6000f;
4919             image->chromaticity.blue_primary.x=0.1500f;
4920             image->chromaticity.blue_primary.y=0.0600f;
4921             image->chromaticity.white_point.x=0.3127f;
4922             image->chromaticity.white_point.y=0.3290f;
4923           }
4924 
4925         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4926         continue;
4927       }
4928 
4929     if (memcmp(type,mng_oFFs,4) == 0)
4930       {
4931         if (length > 8)
4932           {
4933             image->page.x=(ssize_t) mng_get_long(p);
4934             image->page.y=(ssize_t) mng_get_long(&p[4]);
4935 
4936             if ((int) p[8] != 0)
4937               {
4938                 image->page.x/=10000;
4939                 image->page.y/=10000;
4940               }
4941           }
4942 
4943         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4944 
4945         continue;
4946       }
4947 
4948     if (memcmp(type,mng_pHYs,4) == 0)
4949       {
4950         if (length > 8)
4951           {
4952             image->resolution.x=(double) mng_get_long(p);
4953             image->resolution.y=(double) mng_get_long(&p[4]);
4954             if ((int) p[8] == PNG_RESOLUTION_METER)
4955               {
4956                 image->units=PixelsPerCentimeterResolution;
4957                 image->resolution.x=image->resolution.x/100.0f;
4958                 image->resolution.y=image->resolution.y/100.0f;
4959               }
4960           }
4961 
4962         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4963         continue;
4964       }
4965 
4966 #if 0
4967     if (memcmp(type,mng_iCCP,4) == 0)
4968       {
4969         /* To do: */
4970         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4971 
4972         continue;
4973       }
4974 #endif
4975 
4976     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4977 
4978     if (memcmp(type,mng_IEND,4))
4979       continue;
4980 
4981     break;
4982   }
4983 
4984 
4985   /* IEND found */
4986 
4987   /*
4988     Finish up reading image data:
4989 
4990        o read main image from color_blob.
4991 
4992        o close color_blob.
4993 
4994        o if (color_type has alpha)
4995             if alpha_encoding is PNG
4996                read secondary image from alpha_blob via ReadPNG
4997             if alpha_encoding is JPEG
4998                read secondary image from alpha_blob via ReadJPEG
4999 
5000        o close alpha_blob.
5001 
5002        o copy intensity of secondary image into
5003          alpha samples of main image.
5004 
5005        o destroy the secondary image.
5006   */
5007 
5008   if (color_image_info == (ImageInfo *) NULL)
5009     {
5010       assert(color_image == (Image *) NULL);
5011       assert(alpha_image == (Image *) NULL);
5012       if (color_image != (Image *) NULL)
5013         color_image=DestroyImageList(color_image);
5014       return(DestroyImageList(image));
5015     }
5016 
5017   if (color_image == (Image *) NULL)
5018     {
5019       assert(alpha_image == (Image *) NULL);
5020       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5021     }
5022 
5023   (void) SeekBlob(color_image,0,SEEK_SET);
5024 
5025   if (logging != MagickFalse)
5026     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5027       "    Reading jng_image from color_blob.");
5028 
5029   assert(color_image_info != (ImageInfo *) NULL);
5030   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,
5031     "jpeg:%s",color_image->filename);
5032 
5033   color_image_info->ping=MagickFalse;   /* To do: avoid this */
5034   jng_image=ReadImage(color_image_info,exception);
5035 
5036   (void) RelinquishUniqueFileResource(color_image->filename);
5037   color_image=DestroyImage(color_image);
5038   color_image_info=DestroyImageInfo(color_image_info);
5039 
5040   if (jng_image == (Image *) NULL)
5041   {
5042     DestroyJNG(NULL,NULL,NULL,&alpha_image,&alpha_image_info);
5043     return(DestroyImageList(image));
5044   }
5045 
5046 
5047   if (logging != MagickFalse)
5048     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5049       "    Copying jng_image pixels to main image.");
5050 
5051   image->rows=jng_height;
5052   image->columns=jng_width;
5053 
5054   status=SetImageExtent(image,image->columns,image->rows,exception);
5055   if (status == MagickFalse)
5056     {
5057       DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5058         &alpha_image_info);
5059       jng_image=DestroyImageList(jng_image);
5060       return(DestroyImageList(image));
5061     }
5062   if ((image->columns != jng_image->columns) ||
5063       (image->rows != jng_image->rows))
5064     {
5065       DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5066         &alpha_image_info);
5067       jng_image=DestroyImageList(jng_image);
5068       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5069     }
5070   for (y=0; y < (ssize_t) image->rows; y++)
5071   {
5072     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5073     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5074     if ((s == (const Quantum *)  NULL) || (q == (Quantum *) NULL))
5075       break;
5076     for (x=(ssize_t) image->columns; x != 0; x--)
5077     {
5078       SetPixelRed(image,GetPixelRed(jng_image,s),q);
5079       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5080       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5081       q+=GetPixelChannels(image);
5082       s+=GetPixelChannels(jng_image);
5083     }
5084 
5085     if (SyncAuthenticPixels(image,exception) == MagickFalse)
5086       break;
5087   }
5088 
5089   jng_image=DestroyImage(jng_image);
5090 
5091   if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
5092     {
5093       if (jng_alpha_compression_method == 0)
5094         {
5095           png_byte
5096             data[5];
5097           (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5098           PNGType(data,mng_IEND);
5099           LogPNGChunk(logging,mng_IEND,0L);
5100           (void) WriteBlob(alpha_image,4,data);
5101           (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5102         }
5103 
5104       (void) CloseBlob(alpha_image);
5105 
5106       if (logging != MagickFalse)
5107         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5108           "    Reading alpha from alpha_blob.");
5109 
5110       (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5111         "%s",alpha_image->filename);
5112 
5113       jng_image=ReadImage(alpha_image_info,exception);
5114 
5115       if (jng_image != (Image *) NULL)
5116         for (y=0; y < (ssize_t) image->rows; y++)
5117         {
5118           s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5119           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5120           if ((s == (const Quantum *)  NULL) || (q == (Quantum *) NULL))
5121             break;
5122 
5123           if (image->alpha_trait != UndefinedPixelTrait)
5124             for (x=(ssize_t) image->columns; x != 0; x--)
5125             {
5126               SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5127               q+=GetPixelChannels(image);
5128               s+=GetPixelChannels(jng_image);
5129             }
5130 
5131           else
5132             for (x=(ssize_t) image->columns; x != 0; x--)
5133             {
5134               SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5135               if (GetPixelAlpha(image,q) != OpaqueAlpha)
5136                 image->alpha_trait=BlendPixelTrait;
5137               q+=GetPixelChannels(image);
5138               s+=GetPixelChannels(jng_image);
5139             }
5140 
5141           if (SyncAuthenticPixels(image,exception) == MagickFalse)
5142             break;
5143         }
5144       (void) RelinquishUniqueFileResource(alpha_image->filename);
5145       alpha_image=DestroyImage(alpha_image);
5146       alpha_image_info=DestroyImageInfo(alpha_image_info);
5147       if (jng_image != (Image *) NULL)
5148         jng_image=DestroyImage(jng_image);
5149     }
5150 
5151   /* Read the JNG image.  */
5152 
5153   if (mng_info->mng_type == 0)
5154     {
5155       mng_info->mng_width=jng_width;
5156       mng_info->mng_height=jng_height;
5157     }
5158 
5159   if (image->page.width == 0 && image->page.height == 0)
5160     {
5161       image->page.width=jng_width;
5162       image->page.height=jng_height;
5163     }
5164 
5165   if (image->page.x == 0 && image->page.y == 0)
5166     {
5167       image->page.x=mng_info->x_off[mng_info->object_id];
5168       image->page.y=mng_info->y_off[mng_info->object_id];
5169     }
5170 
5171   else
5172     {
5173       image->page.y=mng_info->y_off[mng_info->object_id];
5174     }
5175 
5176   mng_info->image_found++;
5177   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5178     2*GetBlobSize(image));
5179 
5180   if (status == MagickFalse)
5181     return(DestroyImageList(image));
5182 
5183   if (logging != MagickFalse)
5184     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5185       "  exit ReadOneJNGImage()");
5186 
5187   return(image);
5188 }
5189 
5190 /*
5191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5192 %                                                                             %
5193 %                                                                             %
5194 %                                                                             %
5195 %   R e a d J N G I m a g e                                                   %
5196 %                                                                             %
5197 %                                                                             %
5198 %                                                                             %
5199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5200 %
5201 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5202 %  (including the 8-byte signature)  and returns it.  It allocates the memory
5203 %  necessary for the new Image structure and returns a pointer to the new
5204 %  image.
5205 %
5206 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
5207 %
5208 %  The format of the ReadJNGImage method is:
5209 %
5210 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5211 %         *exception)
5212 %
5213 %  A description of each parameter follows:
5214 %
5215 %    o image_info: the image info.
5216 %
5217 %    o exception: return any errors or warnings in this structure.
5218 %
5219 */
5220 
ReadJNGImage(const ImageInfo * image_info,ExceptionInfo * exception)5221 static Image *ReadJNGImage(const ImageInfo *image_info,
5222                 ExceptionInfo *exception)
5223 {
5224   Image
5225     *image;
5226 
5227   MagickBooleanType
5228     logging,
5229     status;
5230 
5231   MngInfo
5232     *mng_info;
5233 
5234   char
5235     magic_number[MagickPathExtent];
5236 
5237   size_t
5238     count;
5239 
5240   /*
5241     Open image file.
5242   */
5243   assert(image_info != (const ImageInfo *) NULL);
5244   assert(image_info->signature == MagickCoreSignature);
5245   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5246      image_info->filename);
5247   assert(exception != (ExceptionInfo *) NULL);
5248   assert(exception->signature == MagickCoreSignature);
5249   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5250   image=AcquireImage(image_info,exception);
5251   mng_info=(MngInfo *) NULL;
5252   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5253 
5254   if (status == MagickFalse)
5255     return(DestroyImageList(image));
5256 
5257   if (LocaleCompare(image_info->magick,"JNG") != 0)
5258     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5259 
5260   /* Verify JNG signature.  */
5261 
5262   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5263 
5264   if ((count < 8) || (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0))
5265     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5266 
5267   /*
5268      Verify that file size large enough to contain a JNG datastream.
5269   */
5270   if (GetBlobSize(image) < 147)
5271     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5272 
5273   /* Allocate a MngInfo structure.  */
5274 
5275   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5276 
5277   if (mng_info == (MngInfo *) NULL)
5278     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5279 
5280   /* Initialize members of the MngInfo structure.  */
5281 
5282   (void) memset(mng_info,0,sizeof(MngInfo));
5283 
5284   mng_info->image=image;
5285   image=ReadOneJNGImage(mng_info,image_info,exception);
5286   mng_info=MngInfoFreeStruct(mng_info);
5287 
5288   if (image == (Image *) NULL)
5289     {
5290       if (logging != MagickFalse)
5291         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5292           "exit ReadJNGImage() with error");
5293 
5294       return((Image *) NULL);
5295     }
5296   (void) CloseBlob(image);
5297 
5298   if (image->columns == 0 || image->rows == 0)
5299     {
5300       if (logging != MagickFalse)
5301         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5302           "exit ReadJNGImage() with error");
5303 
5304       ThrowReaderException(CorruptImageError,"CorruptImage");
5305     }
5306 
5307   if (logging != MagickFalse)
5308     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5309 
5310   return(image);
5311 }
5312 #endif
5313 
ReadOneMNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)5314 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5315      ExceptionInfo *exception)
5316 {
5317   char
5318     page_geometry[MagickPathExtent];
5319 
5320   Image
5321     *image;
5322 
5323   MagickBooleanType
5324     logging;
5325 
5326   volatile int
5327     first_mng_object,
5328     object_id,
5329     term_chunk_found,
5330     skip_to_iend;
5331 
5332   volatile ssize_t
5333     image_count=0;
5334 
5335   MagickBooleanType
5336     status;
5337 
5338   MagickOffsetType
5339     offset;
5340 
5341   MngBox
5342     default_fb,
5343     fb,
5344     previous_fb;
5345 
5346 #if defined(MNG_INSERT_LAYERS)
5347   PixelInfo
5348     mng_background_color;
5349 #endif
5350 
5351   register unsigned char
5352     *p;
5353 
5354   register ssize_t
5355     i;
5356 
5357   size_t
5358     count;
5359 
5360   ssize_t
5361     loop_level;
5362 
5363   volatile short
5364     skipping_loop;
5365 
5366 #if defined(MNG_INSERT_LAYERS)
5367   unsigned int
5368     mandatory_back=0;
5369 #endif
5370 
5371   volatile unsigned int
5372 #ifdef MNG_OBJECT_BUFFERS
5373     mng_background_object=0,
5374 #endif
5375     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5376 
5377   size_t
5378     default_frame_timeout,
5379     frame_timeout,
5380 #if defined(MNG_INSERT_LAYERS)
5381     image_height,
5382     image_width,
5383 #endif
5384     length;
5385 
5386   /* These delays are all measured in image ticks_per_second,
5387    * not in MNG ticks_per_second
5388    */
5389   volatile size_t
5390     default_frame_delay,
5391     final_delay,
5392     final_image_delay,
5393     frame_delay,
5394 #if defined(MNG_INSERT_LAYERS)
5395     insert_layers,
5396 #endif
5397     mng_iterations=1,
5398     simplicity=0,
5399     subframe_height=0,
5400     subframe_width=0;
5401 
5402   previous_fb.top=0;
5403   previous_fb.bottom=0;
5404   previous_fb.left=0;
5405   previous_fb.right=0;
5406   default_fb.top=0;
5407   default_fb.bottom=0;
5408   default_fb.left=0;
5409   default_fb.right=0;
5410 
5411   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5412     "  Enter ReadOneMNGImage()");
5413 
5414   image=mng_info->image;
5415 
5416   if (LocaleCompare(image_info->magick,"MNG") == 0)
5417     {
5418       char
5419         magic_number[MagickPathExtent];
5420 
5421       /* Verify MNG signature.  */
5422       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5423       if ((count < 8) || (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0))
5424         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5425 
5426       /* Initialize some nonzero members of the MngInfo structure.  */
5427       for (i=0; i < MNG_MAX_OBJECTS; i++)
5428       {
5429         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5430         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5431       }
5432       mng_info->exists[0]=MagickTrue;
5433     }
5434 
5435   skipping_loop=(-1);
5436   first_mng_object=MagickTrue;
5437   mng_type=0;
5438 #if defined(MNG_INSERT_LAYERS)
5439   insert_layers=MagickFalse; /* should be False during convert or mogrify */
5440 #endif
5441   default_frame_delay=0;
5442   default_frame_timeout=0;
5443   frame_delay=0;
5444   final_delay=1;
5445   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5446   object_id=0;
5447   skip_to_iend=MagickFalse;
5448   term_chunk_found=MagickFalse;
5449   mng_info->framing_mode=1;
5450 #if defined(MNG_INSERT_LAYERS)
5451   mandatory_back=MagickFalse;
5452 #endif
5453 #if defined(MNG_INSERT_LAYERS)
5454   mng_background_color=image->background_color;
5455 #endif
5456   default_fb=mng_info->frame;
5457   previous_fb=mng_info->frame;
5458   do
5459   {
5460     char
5461       type[MagickPathExtent];
5462 
5463     if (LocaleCompare(image_info->magick,"MNG") == 0)
5464       {
5465         unsigned char
5466           *chunk;
5467 
5468         /*
5469           Read a new chunk.
5470         */
5471         type[0]='\0';
5472         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5473         length=(size_t) ReadBlobMSBLong(image);
5474         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5475 
5476         if (logging != MagickFalse)
5477           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5478            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5479            type[0],type[1],type[2],type[3],(double) length);
5480 
5481         if ((length > PNG_UINT_31_MAX) || (length > GetBlobSize(image)) ||
5482             (count < 4))
5483           ThrowReaderException(CorruptImageError,"CorruptImage");
5484 
5485         p=NULL;
5486         chunk=(unsigned char *) NULL;
5487 
5488         if (length != 0)
5489           {
5490             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5491 
5492             if (chunk == (unsigned char *) NULL)
5493               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5494 
5495             for (i=0; i < (ssize_t) length; i++)
5496             {
5497               int
5498                 c;
5499 
5500               c=ReadBlobByte(image);
5501               if (c == EOF)
5502                 {
5503                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5504                   ThrowReaderException(CorruptImageError,
5505                     "InsufficientImageDataInFile");
5506                 }
5507               chunk[i]=(unsigned char) c;
5508             }
5509 
5510             p=chunk;
5511           }
5512 
5513         (void) ReadBlobMSBLong(image);  /* read crc word */
5514 
5515 #if !defined(JNG_SUPPORTED)
5516         if (memcmp(type,mng_JHDR,4) == 0)
5517           {
5518             skip_to_iend=MagickTrue;
5519 
5520             if (mng_info->jhdr_warning == 0)
5521               (void) ThrowMagickException(exception,GetMagickModule(),
5522                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5523 
5524             mng_info->jhdr_warning++;
5525           }
5526 #endif
5527         if (memcmp(type,mng_DHDR,4) == 0)
5528           {
5529             skip_to_iend=MagickTrue;
5530 
5531             if (mng_info->dhdr_warning == 0)
5532               (void) ThrowMagickException(exception,GetMagickModule(),
5533                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5534 
5535             mng_info->dhdr_warning++;
5536           }
5537         if (memcmp(type,mng_MEND,4) == 0)
5538           {
5539             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5540             break;
5541           }
5542 
5543         if (skip_to_iend)
5544           {
5545             if (memcmp(type,mng_IEND,4) == 0)
5546               skip_to_iend=MagickFalse;
5547 
5548             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5549 
5550             if (logging != MagickFalse)
5551               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5552                 "  Skip to IEND.");
5553 
5554             continue;
5555           }
5556 
5557         if (memcmp(type,mng_MHDR,4) == 0)
5558           {
5559             if (length != 28)
5560               {
5561                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5562                 ThrowReaderException(CorruptImageError,"CorruptImage");
5563               }
5564 
5565             mng_info->mng_width=(unsigned long)mng_get_long(p);
5566             mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5567 
5568             if (logging != MagickFalse)
5569               {
5570                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5571                   "  MNG width: %.20g",(double) mng_info->mng_width);
5572                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5573                   "  MNG height: %.20g",(double) mng_info->mng_height);
5574               }
5575 
5576             p+=8;
5577             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5578 
5579             if (mng_info->ticks_per_second == 0)
5580               default_frame_delay=0;
5581 
5582             else
5583               default_frame_delay=1UL*image->ticks_per_second/
5584                 mng_info->ticks_per_second;
5585 
5586             frame_delay=default_frame_delay;
5587             simplicity=0;
5588 
5589             p+=16;
5590             simplicity=(size_t) mng_get_long(p);
5591 
5592             mng_type=1;    /* Full MNG */
5593 
5594             if ((simplicity != 0) && ((simplicity | 11) == 11))
5595               mng_type=2; /* LC */
5596 
5597             if ((simplicity != 0) && ((simplicity | 9) == 9))
5598               mng_type=3; /* VLC */
5599 
5600 #if defined(MNG_INSERT_LAYERS)
5601             if (mng_type != 3)
5602               insert_layers=MagickTrue;
5603 #endif
5604             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5605               {
5606                 /* Allocate next image structure.  */
5607                 AcquireNextImage(image_info,image,exception);
5608 
5609                 if (GetNextImageInList(image) == (Image *) NULL)
5610                   return((Image *) NULL);
5611 
5612                 image=SyncNextImageInList(image);
5613                 mng_info->image=image;
5614               }
5615 
5616             if ((mng_info->mng_width > 65535L) ||
5617                 (mng_info->mng_height > 65535L))
5618               {
5619                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5620                 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5621               }
5622 
5623             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5624               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5625               mng_info->mng_height);
5626 
5627             mng_info->frame.left=0;
5628             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5629             mng_info->frame.top=0;
5630             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5631             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5632 
5633             for (i=0; i < MNG_MAX_OBJECTS; i++)
5634               mng_info->object_clip[i]=mng_info->frame;
5635 
5636             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5637             continue;
5638           }
5639 
5640         if (memcmp(type,mng_TERM,4) == 0)
5641           {
5642             int
5643               repeat=0;
5644 
5645             if (length != 0)
5646               repeat=p[0];
5647 
5648             if (repeat == 3 && length > 9)
5649               {
5650                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5651                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5652 
5653                 if (mng_iterations == PNG_UINT_31_MAX)
5654                   mng_iterations=0;
5655 
5656                 image->iterations=mng_iterations;
5657                 term_chunk_found=MagickTrue;
5658               }
5659 
5660             if (logging != MagickFalse)
5661               {
5662                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5663                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5664                   repeat,(double) final_delay, (double) image->iterations);
5665               }
5666 
5667             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5668             continue;
5669           }
5670         if (memcmp(type,mng_DEFI,4) == 0)
5671           {
5672             if (mng_type == 3)
5673               {
5674                 (void) ThrowMagickException(exception,GetMagickModule(),
5675                   CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5676                   image->filename);
5677                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5678                 continue;
5679               }
5680 
5681             if (length < 2)
5682               {
5683                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5684                 ThrowReaderException(CorruptImageError,"CorruptImage");
5685               }
5686 
5687             object_id=((unsigned int) p[0] << 8) | (unsigned int) p[1];
5688 
5689             if (mng_type == 2 && object_id != 0)
5690               (void) ThrowMagickException(exception,GetMagickModule(),
5691                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5692                 image->filename);
5693 
5694             if (object_id >= MNG_MAX_OBJECTS)
5695               {
5696                 /*
5697                   Instead of using a warning we should allocate a larger
5698                   MngInfo structure and continue.
5699                 */
5700                 (void) ThrowMagickException(exception,GetMagickModule(),
5701                   CoderError,"object id too large","`%s'",image->filename);
5702                 object_id=MNG_MAX_OBJECTS-1;
5703               }
5704 
5705             if (mng_info->exists[object_id])
5706               if (mng_info->frozen[object_id])
5707                 {
5708                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5709                   (void) ThrowMagickException(exception,
5710                     GetMagickModule(),CoderError,
5711                     "DEFI cannot redefine a frozen MNG object","`%s'",
5712                     image->filename);
5713                   continue;
5714                 }
5715 
5716             mng_info->exists[object_id]=MagickTrue;
5717 
5718             if (length > 2)
5719               mng_info->invisible[object_id]=p[2];
5720 
5721             /*
5722               Extract object offset info.
5723             */
5724             if (length > 11)
5725               {
5726                 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5727                 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5728                 if (logging != MagickFalse)
5729                   {
5730                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5731                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5732                       object_id,(double) mng_info->x_off[object_id],
5733                       object_id,(double) mng_info->y_off[object_id]);
5734                   }
5735               }
5736 
5737             /*
5738               Extract object clipping info.
5739             */
5740             if (length > 27)
5741               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5742                 &p[12]);
5743 
5744             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5745             continue;
5746           }
5747         if (memcmp(type,mng_bKGD,4) == 0)
5748           {
5749             mng_info->have_global_bkgd=MagickFalse;
5750 
5751             if (length > 5)
5752               {
5753                 mng_info->mng_global_bkgd.red=
5754                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5755 
5756                 mng_info->mng_global_bkgd.green=
5757                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5758 
5759                 mng_info->mng_global_bkgd.blue=
5760                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5761 
5762                 mng_info->have_global_bkgd=MagickTrue;
5763               }
5764 
5765             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5766             continue;
5767           }
5768         if (memcmp(type,mng_BACK,4) == 0)
5769           {
5770 #if defined(MNG_INSERT_LAYERS)
5771             if (length > 6)
5772               mandatory_back=p[6];
5773 
5774             else
5775               mandatory_back=0;
5776 
5777             if (mandatory_back && length > 5)
5778               {
5779                 mng_background_color.red=
5780                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5781 
5782                 mng_background_color.green=
5783                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5784 
5785                 mng_background_color.blue=
5786                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5787 
5788                 mng_background_color.alpha=OpaqueAlpha;
5789               }
5790 
5791 #ifdef MNG_OBJECT_BUFFERS
5792             if (length > 8)
5793               mng_background_object=(p[7] << 8) | p[8];
5794 #endif
5795 #endif
5796             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5797             continue;
5798           }
5799 
5800         if (memcmp(type,mng_PLTE,4) == 0)
5801           {
5802             /* Read global PLTE.  */
5803 
5804             if (length && (length < 769))
5805               {
5806                 /* Read global PLTE.  */
5807 
5808                 if (mng_info->global_plte == (png_colorp) NULL)
5809                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5810                     sizeof(*mng_info->global_plte));
5811 
5812                 if (mng_info->global_plte == (png_colorp) NULL)
5813                   {
5814                     mng_info->global_plte_length=0;
5815                     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5816                     ThrowReaderException(ResourceLimitError,
5817                       "MemoryAllocationFailed");
5818                   }
5819 
5820                 for (i=0; i < (ssize_t) (length/3); i++)
5821                 {
5822                   mng_info->global_plte[i].red=p[3*i];
5823                   mng_info->global_plte[i].green=p[3*i+1];
5824                   mng_info->global_plte[i].blue=p[3*i+2];
5825                 }
5826 
5827                 mng_info->global_plte_length=(unsigned int) (length/3);
5828               }
5829 #ifdef MNG_LOOSE
5830             for ( ; i < 256; i++)
5831             {
5832               mng_info->global_plte[i].red=i;
5833               mng_info->global_plte[i].green=i;
5834               mng_info->global_plte[i].blue=i;
5835             }
5836 
5837             if (length != 0)
5838               mng_info->global_plte_length=256;
5839 #endif
5840             else
5841               mng_info->global_plte_length=0;
5842 
5843             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5844             continue;
5845           }
5846 
5847         if (memcmp(type,mng_tRNS,4) == 0)
5848           {
5849             /* read global tRNS */
5850 
5851             if (length > 0 && length < 257)
5852               for (i=0; i < (ssize_t) length; i++)
5853                 mng_info->global_trns[i]=p[i];
5854 
5855 #ifdef MNG_LOOSE
5856             for ( ; i < 256; i++)
5857               mng_info->global_trns[i]=255;
5858 #endif
5859             mng_info->global_trns_length=(unsigned int) length;
5860             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5861             continue;
5862           }
5863         if (memcmp(type,mng_gAMA,4) == 0)
5864           {
5865             if (length == 4)
5866               {
5867                 ssize_t
5868                   igamma;
5869 
5870                 igamma=mng_get_long(p);
5871                 mng_info->global_gamma=((float) igamma)*0.00001;
5872                 mng_info->have_global_gama=MagickTrue;
5873               }
5874 
5875             else
5876               mng_info->have_global_gama=MagickFalse;
5877 
5878             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5879             continue;
5880           }
5881 
5882         if (memcmp(type,mng_cHRM,4) == 0)
5883           {
5884             /* Read global cHRM */
5885 
5886             if (length == 32)
5887               {
5888                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5889                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5890                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5891                 mng_info->global_chrm.red_primary.y=0.00001*
5892                   mng_get_long(&p[12]);
5893                 mng_info->global_chrm.green_primary.x=0.00001*
5894                   mng_get_long(&p[16]);
5895                 mng_info->global_chrm.green_primary.y=0.00001*
5896                   mng_get_long(&p[20]);
5897                 mng_info->global_chrm.blue_primary.x=0.00001*
5898                   mng_get_long(&p[24]);
5899                 mng_info->global_chrm.blue_primary.y=0.00001*
5900                   mng_get_long(&p[28]);
5901                 mng_info->have_global_chrm=MagickTrue;
5902               }
5903             else
5904               mng_info->have_global_chrm=MagickFalse;
5905 
5906             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5907             continue;
5908           }
5909 
5910         if (memcmp(type,mng_sRGB,4) == 0)
5911           {
5912             /*
5913               Read global sRGB.
5914             */
5915             if (length != 0)
5916               {
5917                 mng_info->global_srgb_intent=
5918                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5919                 mng_info->have_global_srgb=MagickTrue;
5920               }
5921             else
5922               mng_info->have_global_srgb=MagickFalse;
5923 
5924             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5925             continue;
5926           }
5927 
5928         if (memcmp(type,mng_iCCP,4) == 0)
5929           {
5930             /* To do: */
5931 
5932             /*
5933               Read global iCCP.
5934             */
5935             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5936 
5937             continue;
5938           }
5939 
5940         if (memcmp(type,mng_FRAM,4) == 0)
5941           {
5942             if (mng_type == 3)
5943               (void) ThrowMagickException(exception,GetMagickModule(),
5944                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5945                 image->filename);
5946 
5947             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5948               image->delay=frame_delay;
5949 
5950             frame_delay=default_frame_delay;
5951             frame_timeout=default_frame_timeout;
5952             fb=default_fb;
5953 
5954             if (length != 0)
5955               if (p[0])
5956                 mng_info->framing_mode=p[0];
5957 
5958             if (logging != MagickFalse)
5959               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5960                 "    Framing_mode=%d",mng_info->framing_mode);
5961 
5962             if (length > 6)
5963               {
5964                 /* Note the delay and frame clipping boundaries.  */
5965 
5966                 p++; /* framing mode */
5967 
5968                 while (((p-chunk) < (long) length) && *p)
5969                   p++;  /* frame name */
5970 
5971                 p++;  /* frame name terminator */
5972 
5973                 if ((p-chunk) < (ssize_t) (length-4))
5974                   {
5975                     int
5976                       change_delay,
5977                       change_timeout,
5978                       change_clipping;
5979 
5980                     change_delay=(*p++);
5981                     change_timeout=(*p++);
5982                     change_clipping=(*p++);
5983                     p++; /* change_sync */
5984 
5985                     if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5986                       {
5987                         frame_delay=1UL*image->ticks_per_second*
5988                           mng_get_long(p);
5989 
5990                         if (mng_info->ticks_per_second != 0)
5991                           frame_delay/=mng_info->ticks_per_second;
5992 
5993                         else
5994                           frame_delay=PNG_UINT_31_MAX;
5995 
5996                         if (change_delay == 2)
5997                           default_frame_delay=frame_delay;
5998 
5999                         p+=4;
6000 
6001                         if (logging != MagickFalse)
6002                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6003                             "    Framing_delay=%.20g",(double) frame_delay);
6004                       }
6005 
6006                     if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
6007                       {
6008                         frame_timeout=1UL*image->ticks_per_second*
6009                           mng_get_long(p);
6010 
6011                         if (mng_info->ticks_per_second != 0)
6012                           frame_timeout/=mng_info->ticks_per_second;
6013 
6014                         else
6015                           frame_timeout=PNG_UINT_31_MAX;
6016 
6017                         if (change_timeout == 2)
6018                           default_frame_timeout=frame_timeout;
6019 
6020                         p+=4;
6021 
6022                         if (logging != MagickFalse)
6023                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6024                             "    Framing_timeout=%.20g",(double) frame_timeout);
6025                       }
6026 
6027                     if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
6028                       {
6029                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
6030                         p+=16;
6031                         previous_fb=fb;
6032 
6033                         if (logging != MagickFalse)
6034                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6035                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
6036                             (double) fb.left,(double) fb.right,(double) fb.top,
6037                             (double) fb.bottom);
6038 
6039                         if (change_clipping == 2)
6040                           default_fb=fb;
6041                       }
6042                   }
6043               }
6044             mng_info->clip=fb;
6045             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6046 
6047             subframe_width=(size_t) (mng_info->clip.right
6048                -mng_info->clip.left);
6049 
6050             subframe_height=(size_t) (mng_info->clip.bottom
6051                -mng_info->clip.top);
6052             /*
6053               Insert a background layer behind the frame if framing_mode is 4.
6054             */
6055 #if defined(MNG_INSERT_LAYERS)
6056             if (logging != MagickFalse)
6057               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6058                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
6059                 subframe_width,(double) subframe_height);
6060 
6061             if (insert_layers && (mng_info->framing_mode == 4) &&
6062                 (subframe_width) && (subframe_height))
6063               {
6064                 /* Allocate next image structure.  */
6065                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6066                   {
6067                     AcquireNextImage(image_info,image,exception);
6068 
6069                     if (GetNextImageInList(image) == (Image *) NULL)
6070                       return(DestroyImageList(image));
6071 
6072                     image=SyncNextImageInList(image);
6073                   }
6074 
6075                 mng_info->image=image;
6076 
6077                 if (term_chunk_found)
6078                   {
6079                     image->start_loop=MagickTrue;
6080                     image->iterations=mng_iterations;
6081                     term_chunk_found=MagickFalse;
6082                   }
6083 
6084                 else
6085                     image->start_loop=MagickFalse;
6086 
6087                 image->columns=subframe_width;
6088                 image->rows=subframe_height;
6089                 image->page.width=subframe_width;
6090                 image->page.height=subframe_height;
6091                 image->page.x=mng_info->clip.left;
6092                 image->page.y=mng_info->clip.top;
6093                 image->background_color=mng_background_color;
6094                 image->alpha_trait=UndefinedPixelTrait;
6095                 image->delay=0;
6096                 if (SetImageBackgroundColor(image,exception) == MagickFalse)
6097                   {
6098                     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6099                     return(DestroyImageList(image));
6100                   }
6101                 if (logging != MagickFalse)
6102                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6103                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6104                     (double) mng_info->clip.left,
6105                     (double) mng_info->clip.right,
6106                     (double) mng_info->clip.top,
6107                     (double) mng_info->clip.bottom);
6108               }
6109 #endif
6110             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6111             continue;
6112           }
6113 
6114         if (memcmp(type,mng_CLIP,4) == 0)
6115           {
6116             unsigned int
6117               first_object,
6118               last_object;
6119 
6120             /*
6121               Read CLIP.
6122             */
6123             if (length > 3)
6124               {
6125                 first_object=(p[0] << 8) | p[1];
6126                 last_object=(p[2] << 8) | p[3];
6127                 p+=4;
6128 
6129                 for (i=(int) first_object; i <= (int) last_object; i++)
6130                 {
6131                   if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6132                     continue;
6133 
6134                   if (mng_info->exists[i] && !mng_info->frozen[i])
6135                     {
6136                       MngBox
6137                         box;
6138 
6139                       box=mng_info->object_clip[i];
6140                       if ((p-chunk) < (ssize_t) (length-17))
6141                         mng_info->object_clip[i]=
6142                            mng_read_box(box,(char) p[0],&p[1]);
6143                     }
6144                 }
6145 
6146               }
6147             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6148             continue;
6149           }
6150 
6151         if (memcmp(type,mng_SAVE,4) == 0)
6152           {
6153             for (i=1; i < MNG_MAX_OBJECTS; i++)
6154               if (mng_info->exists[i])
6155                 {
6156                  mng_info->frozen[i]=MagickTrue;
6157 #ifdef MNG_OBJECT_BUFFERS
6158                  if (mng_info->ob[i] != (MngBuffer *) NULL)
6159                     mng_info->ob[i]->frozen=MagickTrue;
6160 #endif
6161                 }
6162 
6163             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6164 
6165             continue;
6166           }
6167 
6168         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6169           {
6170             /* Read DISC or SEEK.  */
6171 
6172             if ((length == 0) || (length % 2) || !memcmp(type,mng_SEEK,4))
6173               {
6174                 for (i=1; i < MNG_MAX_OBJECTS; i++)
6175                   MngInfoDiscardObject(mng_info,i);
6176               }
6177 
6178             else
6179               {
6180                 register ssize_t
6181                   j;
6182 
6183                 for (j=1; j < (ssize_t) length; j+=2)
6184                 {
6185                   i=p[j-1] << 8 | p[j];
6186                   MngInfoDiscardObject(mng_info,i);
6187                 }
6188               }
6189 
6190             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6191 
6192             continue;
6193           }
6194 
6195         if (memcmp(type,mng_MOVE,4) == 0)
6196           {
6197             size_t
6198               first_object,
6199               last_object;
6200 
6201             /* read MOVE */
6202 
6203             if (length > 3)
6204             {
6205               first_object=(p[0] << 8) | p[1];
6206               last_object=(p[2] << 8) | p[3];
6207               p+=4;
6208 
6209               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6210               {
6211                 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6212                   continue;
6213 
6214                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6215                     (p-chunk) < (ssize_t) (length-8))
6216                   {
6217                     MngPair
6218                       new_pair;
6219 
6220                     MngPair
6221                       old_pair;
6222 
6223                     old_pair.a=mng_info->x_off[i];
6224                     old_pair.b=mng_info->y_off[i];
6225                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6226                     mng_info->x_off[i]=new_pair.a;
6227                     mng_info->y_off[i]=new_pair.b;
6228                   }
6229               }
6230             }
6231 
6232             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6233             continue;
6234           }
6235 
6236         if (memcmp(type,mng_LOOP,4) == 0)
6237           {
6238             ssize_t loop_iters=1;
6239             if (length > 4)
6240               {
6241                 loop_level=chunk[0];
6242                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
6243 
6244                 /* Record starting point.  */
6245                 loop_iters=mng_get_long(&chunk[1]);
6246 
6247                 if (logging != MagickFalse)
6248                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6249                     "  LOOP level %.20g has %.20g iterations ",
6250                     (double) loop_level, (double) loop_iters);
6251 
6252                 if (loop_iters <= 0)
6253                   skipping_loop=loop_level;
6254 
6255                 else
6256                   {
6257                     if ((MagickSizeType) loop_iters > GetMagickResourceLimit(ListLengthResource))
6258                       loop_iters=GetMagickResourceLimit(ListLengthResource);
6259                     if (loop_iters >= 2147483647L)
6260                       loop_iters=2147483647L;
6261                     mng_info->loop_jump[loop_level]=TellBlob(image);
6262                     mng_info->loop_count[loop_level]=loop_iters;
6263                   }
6264 
6265                 mng_info->loop_iteration[loop_level]=0;
6266               }
6267             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6268             continue;
6269           }
6270 
6271         if (memcmp(type,mng_ENDL,4) == 0)
6272           {
6273             if (length > 0)
6274               {
6275                 loop_level=chunk[0];
6276 
6277                 if (skipping_loop > 0)
6278                   {
6279                     if (skipping_loop == loop_level)
6280                       {
6281                         /*
6282                           Found end of zero-iteration loop.
6283                         */
6284                         skipping_loop=(-1);
6285                         mng_info->loop_active[loop_level]=0;
6286                       }
6287                   }
6288 
6289                 else
6290                   {
6291                     if (mng_info->loop_active[loop_level] == 1)
6292                       {
6293                         mng_info->loop_count[loop_level]--;
6294                         mng_info->loop_iteration[loop_level]++;
6295 
6296                         if (logging != MagickFalse)
6297                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6298                           "  ENDL: LOOP level %.20g has %.20g remaining iters",
6299                             (double) loop_level,(double)
6300                             mng_info->loop_count[loop_level]);
6301 
6302                         if (mng_info->loop_count[loop_level] > 0)
6303                           {
6304                             offset=
6305                               SeekBlob(image,mng_info->loop_jump[loop_level],
6306                               SEEK_SET);
6307 
6308                             if (offset < 0)
6309                               {
6310                                 chunk=(unsigned char *) RelinquishMagickMemory(
6311                                   chunk);
6312                                 ThrowReaderException(CorruptImageError,
6313                                   "ImproperImageHeader");
6314                               }
6315                           }
6316 
6317                         else
6318                           {
6319                             short
6320                               last_level;
6321 
6322                             /*
6323                               Finished loop.
6324                             */
6325                             mng_info->loop_active[loop_level]=0;
6326                             last_level=(-1);
6327                             for (i=0; i < loop_level; i++)
6328                               if (mng_info->loop_active[i] == 1)
6329                                 last_level=(short) i;
6330                             loop_level=last_level;
6331                           }
6332                       }
6333                   }
6334               }
6335 
6336             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6337             continue;
6338           }
6339 
6340         if (memcmp(type,mng_CLON,4) == 0)
6341           {
6342             if (mng_info->clon_warning == 0)
6343               (void) ThrowMagickException(exception,GetMagickModule(),
6344                 CoderError,"CLON is not implemented yet","`%s'",
6345                 image->filename);
6346 
6347             mng_info->clon_warning++;
6348           }
6349 
6350         if (memcmp(type,mng_MAGN,4) == 0)
6351           {
6352             png_uint_16
6353               magn_first,
6354               magn_last,
6355               magn_mb,
6356               magn_ml,
6357               magn_mr,
6358               magn_mt,
6359               magn_mx,
6360               magn_my,
6361               magn_methx,
6362               magn_methy;
6363 
6364             if (length > 1)
6365               magn_first=(p[0] << 8) | p[1];
6366 
6367             else
6368               magn_first=0;
6369 
6370             if (length > 3)
6371               magn_last=(p[2] << 8) | p[3];
6372 
6373             else
6374               magn_last=magn_first;
6375 #ifndef MNG_OBJECT_BUFFERS
6376             if (magn_first || magn_last)
6377               if (mng_info->magn_warning == 0)
6378                 {
6379                   (void) ThrowMagickException(exception,
6380                      GetMagickModule(),CoderError,
6381                      "MAGN is not implemented yet for nonzero objects",
6382                      "`%s'",image->filename);
6383 
6384                    mng_info->magn_warning++;
6385                 }
6386 #endif
6387             if (length > 4)
6388               magn_methx=p[4];
6389 
6390             else
6391               magn_methx=0;
6392 
6393             if (length > 6)
6394               magn_mx=(p[5] << 8) | p[6];
6395 
6396             else
6397               magn_mx=1;
6398 
6399             if (magn_mx == 0)
6400               magn_mx=1;
6401 
6402             if (length > 8)
6403               magn_my=(p[7] << 8) | p[8];
6404 
6405             else
6406               magn_my=magn_mx;
6407 
6408             if (magn_my == 0)
6409               magn_my=1;
6410 
6411             if (length > 10)
6412               magn_ml=(p[9] << 8) | p[10];
6413 
6414             else
6415               magn_ml=magn_mx;
6416 
6417             if (magn_ml == 0)
6418               magn_ml=1;
6419 
6420             if (length > 12)
6421               magn_mr=(p[11] << 8) | p[12];
6422 
6423             else
6424               magn_mr=magn_mx;
6425 
6426             if (magn_mr == 0)
6427               magn_mr=1;
6428 
6429             if (length > 14)
6430               magn_mt=(p[13] << 8) | p[14];
6431 
6432             else
6433               magn_mt=magn_my;
6434 
6435             if (magn_mt == 0)
6436               magn_mt=1;
6437 
6438             if (length > 16)
6439               magn_mb=(p[15] << 8) | p[16];
6440 
6441             else
6442               magn_mb=magn_my;
6443 
6444             if (magn_mb == 0)
6445               magn_mb=1;
6446 
6447             if (length > 17)
6448               magn_methy=p[17];
6449 
6450             else
6451               magn_methy=magn_methx;
6452 
6453 
6454             if (magn_methx > 5 || magn_methy > 5)
6455               if (mng_info->magn_warning == 0)
6456                 {
6457                   (void) ThrowMagickException(exception,
6458                      GetMagickModule(),CoderError,
6459                      "Unknown MAGN method in MNG datastream","`%s'",
6460                      image->filename);
6461 
6462                    mng_info->magn_warning++;
6463                 }
6464 #ifdef MNG_OBJECT_BUFFERS
6465           /* Magnify existing objects in the range magn_first to magn_last */
6466 #endif
6467             if (magn_first == 0 || magn_last == 0)
6468               {
6469                 /* Save the magnification factors for object 0 */
6470                 mng_info->magn_mb=magn_mb;
6471                 mng_info->magn_ml=magn_ml;
6472                 mng_info->magn_mr=magn_mr;
6473                 mng_info->magn_mt=magn_mt;
6474                 mng_info->magn_mx=magn_mx;
6475                 mng_info->magn_my=magn_my;
6476                 mng_info->magn_methx=magn_methx;
6477                 mng_info->magn_methy=magn_methy;
6478               }
6479           }
6480 
6481         if (memcmp(type,mng_PAST,4) == 0)
6482           {
6483             if (mng_info->past_warning == 0)
6484               (void) ThrowMagickException(exception,GetMagickModule(),
6485                 CoderError,"PAST is not implemented yet","`%s'",
6486                 image->filename);
6487 
6488             mng_info->past_warning++;
6489           }
6490 
6491         if (memcmp(type,mng_SHOW,4) == 0)
6492           {
6493             if (mng_info->show_warning == 0)
6494               (void) ThrowMagickException(exception,GetMagickModule(),
6495                 CoderError,"SHOW is not implemented yet","`%s'",
6496                 image->filename);
6497 
6498             mng_info->show_warning++;
6499           }
6500 
6501         if (memcmp(type,mng_sBIT,4) == 0)
6502           {
6503             if (length < 4)
6504               mng_info->have_global_sbit=MagickFalse;
6505 
6506             else
6507               {
6508                 mng_info->global_sbit.gray=p[0];
6509                 mng_info->global_sbit.red=p[0];
6510                 mng_info->global_sbit.green=p[1];
6511                 mng_info->global_sbit.blue=p[2];
6512                 mng_info->global_sbit.alpha=p[3];
6513                 mng_info->have_global_sbit=MagickTrue;
6514              }
6515           }
6516         if (memcmp(type,mng_pHYs,4) == 0)
6517           {
6518             if (length > 8)
6519               {
6520                 mng_info->global_x_pixels_per_unit=
6521                     (size_t) mng_get_long(p);
6522                 mng_info->global_y_pixels_per_unit=
6523                     (size_t) mng_get_long(&p[4]);
6524                 mng_info->global_phys_unit_type=p[8];
6525                 mng_info->have_global_phys=MagickTrue;
6526               }
6527 
6528             else
6529               mng_info->have_global_phys=MagickFalse;
6530           }
6531         if (memcmp(type,mng_pHYg,4) == 0)
6532           {
6533             if (mng_info->phyg_warning == 0)
6534               (void) ThrowMagickException(exception,GetMagickModule(),
6535                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6536 
6537             mng_info->phyg_warning++;
6538           }
6539         if (memcmp(type,mng_BASI,4) == 0)
6540           {
6541             skip_to_iend=MagickTrue;
6542 
6543             if (mng_info->basi_warning == 0)
6544               (void) ThrowMagickException(exception,GetMagickModule(),
6545                 CoderError,"BASI is not implemented yet","`%s'",
6546                 image->filename);
6547 
6548             mng_info->basi_warning++;
6549 #ifdef MNG_BASI_SUPPORTED
6550             basi_width=(unsigned long) mng_get_long(p);
6551             basi_width=(unsigned long) mng_get_long(&p[4]);
6552             basi_color_type=p[8];
6553             basi_compression_method=p[9];
6554             basi_filter_type=p[10];
6555             basi_interlace_method=p[11];
6556             if (length > 11)
6557               basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6558 
6559             else
6560               basi_red=0;
6561 
6562             if (length > 13)
6563               basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6564 
6565             else
6566               basi_green=0;
6567 
6568             if (length > 15)
6569               basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6570 
6571             else
6572               basi_blue=0;
6573 
6574             if (length > 17)
6575               basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6576 
6577             else
6578               {
6579                 if (basi_sample_depth == 16)
6580                   basi_alpha=65535L;
6581                 else
6582                   basi_alpha=255;
6583               }
6584 
6585             if (length > 19)
6586               basi_viewable=p[20];
6587 
6588             else
6589               basi_viewable=0;
6590 
6591 #endif
6592             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6593             continue;
6594           }
6595 
6596         if (memcmp(type,mng_IHDR,4)
6597 #if defined(JNG_SUPPORTED)
6598             && memcmp(type,mng_JHDR,4)
6599 #endif
6600             )
6601           {
6602             /* Not an IHDR or JHDR chunk */
6603             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6604 
6605             continue;
6606           }
6607 /* Process IHDR */
6608         if (logging != MagickFalse)
6609           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6610             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6611 
6612         mng_info->exists[object_id]=MagickTrue;
6613         mng_info->viewable[object_id]=MagickTrue;
6614 
6615         if (mng_info->invisible[object_id])
6616           {
6617             if (logging != MagickFalse)
6618               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6619                 "  Skipping invisible object");
6620 
6621             skip_to_iend=MagickTrue;
6622             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6623             continue;
6624           }
6625 #if defined(MNG_INSERT_LAYERS)
6626         if (length < 8)
6627           {
6628             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6629             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6630           }
6631 
6632         image_width=(size_t) mng_get_long(p);
6633         image_height=(size_t) mng_get_long(&p[4]);
6634 #endif
6635         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6636 
6637         /*
6638           Insert a transparent background layer behind the entire animation
6639           if it is not full screen.
6640         */
6641 #if defined(MNG_INSERT_LAYERS)
6642         if (insert_layers && mng_type && first_mng_object)
6643           {
6644             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6645                 (image_width < mng_info->mng_width) ||
6646                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6647                 (image_height < mng_info->mng_height) ||
6648                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6649               {
6650                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6651                   {
6652                     /*
6653                       Allocate next image structure.
6654                     */
6655                     AcquireNextImage(image_info,image,exception);
6656 
6657                     if (GetNextImageInList(image) == (Image *) NULL)
6658                       return(DestroyImageList(image));
6659 
6660                     image=SyncNextImageInList(image);
6661                   }
6662                 mng_info->image=image;
6663 
6664                 if (term_chunk_found)
6665                   {
6666                     image->start_loop=MagickTrue;
6667                     image->iterations=mng_iterations;
6668                     term_chunk_found=MagickFalse;
6669                   }
6670 
6671                 else
6672                     image->start_loop=MagickFalse;
6673 
6674                 /* Make a background rectangle.  */
6675 
6676                 image->delay=0;
6677                 image->columns=mng_info->mng_width;
6678                 image->rows=mng_info->mng_height;
6679                 image->page.width=mng_info->mng_width;
6680                 image->page.height=mng_info->mng_height;
6681                 image->page.x=0;
6682                 image->page.y=0;
6683                 image->background_color=mng_background_color;
6684                 (void) SetImageBackgroundColor(image,exception);
6685                 if (logging != MagickFalse)
6686                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6687                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6688                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6689               }
6690           }
6691         /*
6692           Insert a background layer behind the upcoming image if
6693           framing_mode is 3, and we haven't already inserted one.
6694         */
6695         if (insert_layers && (mng_info->framing_mode == 3) &&
6696                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6697                 (simplicity & 0x08)))
6698           {
6699             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6700             {
6701               /*
6702                 Allocate next image structure.
6703               */
6704               AcquireNextImage(image_info,image,exception);
6705 
6706               if (GetNextImageInList(image) == (Image *) NULL)
6707                 return(DestroyImageList(image));
6708 
6709               image=SyncNextImageInList(image);
6710             }
6711 
6712             mng_info->image=image;
6713 
6714             if (term_chunk_found)
6715               {
6716                 image->start_loop=MagickTrue;
6717                 image->iterations=mng_iterations;
6718                 term_chunk_found=MagickFalse;
6719               }
6720 
6721             else
6722                 image->start_loop=MagickFalse;
6723 
6724             image->delay=0;
6725             image->columns=subframe_width;
6726             image->rows=subframe_height;
6727             image->page.width=subframe_width;
6728             image->page.height=subframe_height;
6729             image->page.x=mng_info->clip.left;
6730             image->page.y=mng_info->clip.top;
6731             image->background_color=mng_background_color;
6732             image->alpha_trait=UndefinedPixelTrait;
6733             (void) SetImageBackgroundColor(image,exception);
6734 
6735             if (logging != MagickFalse)
6736               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6737                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6738                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6739                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6740           }
6741 #endif /* MNG_INSERT_LAYERS */
6742         first_mng_object=MagickFalse;
6743 
6744         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6745           {
6746             /*
6747               Allocate next image structure.
6748             */
6749             AcquireNextImage(image_info,image,exception);
6750 
6751             if (GetNextImageInList(image) == (Image *) NULL)
6752               return(DestroyImageList(image));
6753 
6754             image=SyncNextImageInList(image);
6755           }
6756         mng_info->image=image;
6757         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6758           GetBlobSize(image));
6759 
6760         if (status == MagickFalse)
6761           break;
6762 
6763         if (term_chunk_found)
6764           {
6765             image->start_loop=MagickTrue;
6766             term_chunk_found=MagickFalse;
6767           }
6768 
6769         else
6770             image->start_loop=MagickFalse;
6771 
6772         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6773           {
6774             image->delay=frame_delay;
6775             frame_delay=default_frame_delay;
6776           }
6777 
6778         else
6779           image->delay=0;
6780 
6781         image->page.width=mng_info->mng_width;
6782         image->page.height=mng_info->mng_height;
6783         image->page.x=mng_info->x_off[object_id];
6784         image->page.y=mng_info->y_off[object_id];
6785         image->iterations=mng_iterations;
6786 
6787         /*
6788           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6789         */
6790 
6791         if (logging != MagickFalse)
6792           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6793             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6794             type[2],type[3]);
6795 
6796         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6797 
6798         if (offset < 0)
6799           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6800       }
6801 
6802     mng_info->image=image;
6803     mng_info->mng_type=mng_type;
6804     mng_info->object_id=object_id;
6805 
6806     if (memcmp(type,mng_IHDR,4) == 0)
6807       image=ReadOnePNGImage(mng_info,image_info,exception);
6808 
6809 #if defined(JNG_SUPPORTED)
6810     else
6811       image=ReadOneJNGImage(mng_info,image_info,exception);
6812 #endif
6813 
6814     if (image == (Image *) NULL)
6815       {
6816         if (logging != MagickFalse)
6817           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6818             "exit ReadJNGImage() with error");
6819 
6820         return((Image *) NULL);
6821       }
6822 
6823     if (image->columns == 0 || image->rows == 0)
6824       {
6825         (void) CloseBlob(image);
6826         return(DestroyImageList(image));
6827       }
6828 
6829     mng_info->image=image;
6830 
6831     if (mng_type)
6832       {
6833         MngBox
6834           crop_box;
6835 
6836         if (mng_info->magn_methx || mng_info->magn_methy)
6837           {
6838             png_uint_32
6839                magnified_height,
6840                magnified_width;
6841 
6842             if (logging != MagickFalse)
6843               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6844                 "  Processing MNG MAGN chunk");
6845 
6846             if (mng_info->magn_methx == 1)
6847               {
6848                 magnified_width=mng_info->magn_ml;
6849 
6850                 if (image->columns > 1)
6851                    magnified_width += mng_info->magn_mr;
6852 
6853                 if (image->columns > 2)
6854                    magnified_width += (png_uint_32)
6855                       ((image->columns-2)*(mng_info->magn_mx));
6856               }
6857 
6858             else
6859               {
6860                 magnified_width=(png_uint_32) image->columns;
6861 
6862                 if (image->columns > 1)
6863                    magnified_width += mng_info->magn_ml-1;
6864 
6865                 if (image->columns > 2)
6866                    magnified_width += mng_info->magn_mr-1;
6867 
6868                 if (image->columns > 3)
6869                    magnified_width += (png_uint_32)
6870                       ((image->columns-3)*(mng_info->magn_mx-1));
6871               }
6872 
6873             if (mng_info->magn_methy == 1)
6874               {
6875                 magnified_height=mng_info->magn_mt;
6876 
6877                 if (image->rows > 1)
6878                    magnified_height += mng_info->magn_mb;
6879 
6880                 if (image->rows > 2)
6881                    magnified_height += (png_uint_32)
6882                       ((image->rows-2)*(mng_info->magn_my));
6883               }
6884 
6885             else
6886               {
6887                 magnified_height=(png_uint_32) image->rows;
6888 
6889                 if (image->rows > 1)
6890                    magnified_height += mng_info->magn_mt-1;
6891 
6892                 if (image->rows > 2)
6893                    magnified_height += mng_info->magn_mb-1;
6894 
6895                 if (image->rows > 3)
6896                    magnified_height += (png_uint_32)
6897                       ((image->rows-3)*(mng_info->magn_my-1));
6898               }
6899 
6900             if (magnified_height > image->rows ||
6901                 magnified_width > image->columns)
6902               {
6903                 Image
6904                   *large_image;
6905 
6906                 int
6907                   yy;
6908 
6909                 Quantum
6910                   *next,
6911                   *prev;
6912 
6913                 png_uint_16
6914                   magn_methx,
6915                   magn_methy;
6916 
6917                 ssize_t
6918                   m,
6919                   y;
6920 
6921                 register Quantum
6922                   *n,
6923                   *q;
6924 
6925                 register ssize_t
6926                   x;
6927 
6928                 /* Allocate next image structure.  */
6929 
6930                 if (logging != MagickFalse)
6931                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6932                     "    Allocate magnified image");
6933 
6934                 AcquireNextImage(image_info,image,exception);
6935 
6936                 if (GetNextImageInList(image) == (Image *) NULL)
6937                   return(DestroyImageList(image));
6938 
6939                 large_image=SyncNextImageInList(image);
6940 
6941                 large_image->columns=magnified_width;
6942                 large_image->rows=magnified_height;
6943 
6944                 magn_methx=mng_info->magn_methx;
6945                 magn_methy=mng_info->magn_methy;
6946 
6947 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6948 #define QM unsigned short
6949                 if (magn_methx != 1 || magn_methy != 1)
6950                   {
6951                   /*
6952                      Scale pixels to unsigned shorts to prevent
6953                      overflow of intermediate values of interpolations
6954                   */
6955                      for (y=0; y < (ssize_t) image->rows; y++)
6956                      {
6957                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6958                           exception);
6959                        if (q == (Quantum *) NULL)
6960                          break;
6961                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6962                        {
6963                           SetPixelRed(image,ScaleQuantumToShort(
6964                             GetPixelRed(image,q)),q);
6965                           SetPixelGreen(image,ScaleQuantumToShort(
6966                             GetPixelGreen(image,q)),q);
6967                           SetPixelBlue(image,ScaleQuantumToShort(
6968                             GetPixelBlue(image,q)),q);
6969                           SetPixelAlpha(image,ScaleQuantumToShort(
6970                             GetPixelAlpha(image,q)),q);
6971                           q+=GetPixelChannels(image);
6972                        }
6973 
6974                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6975                          break;
6976                      }
6977                   }
6978 #else
6979 #define QM Quantum
6980 #endif
6981 
6982                 if (image->alpha_trait != UndefinedPixelTrait)
6983                    (void) SetImageBackgroundColor(large_image,exception);
6984 
6985                 else
6986                   {
6987                     large_image->background_color.alpha=OpaqueAlpha;
6988                     (void) SetImageBackgroundColor(large_image,exception);
6989 
6990                     if (magn_methx == 4)
6991                       magn_methx=2;
6992 
6993                     if (magn_methx == 5)
6994                       magn_methx=3;
6995 
6996                     if (magn_methy == 4)
6997                       magn_methy=2;
6998 
6999                     if (magn_methy == 5)
7000                       magn_methy=3;
7001                   }
7002 
7003                 /* magnify the rows into the right side of the large image */
7004 
7005                 if (logging != MagickFalse)
7006                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7007                     "    Magnify the rows to %.20g",
7008                     (double) large_image->rows);
7009                 m=(ssize_t) mng_info->magn_mt;
7010                 yy=0;
7011                 length=(size_t) GetPixelChannels(image)*image->columns;
7012                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
7013                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
7014 
7015                 if ((prev == (Quantum *) NULL) ||
7016                     (next == (Quantum *) NULL))
7017                   {
7018                     if (prev != (Quantum *) NULL)
7019                       prev=(Quantum *) RelinquishMagickMemory(prev);
7020                     if (next != (Quantum *) NULL)
7021                       next=(Quantum *) RelinquishMagickMemory(next);
7022                     image=DestroyImageList(image);
7023                     ThrowReaderException(ResourceLimitError,
7024                       "MemoryAllocationFailed");
7025                   }
7026 
7027                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
7028                 (void) memcpy(next,n,length);
7029 
7030                 for (y=0; y < (ssize_t) image->rows; y++)
7031                 {
7032                   if (y == 0)
7033                     m=(ssize_t) mng_info->magn_mt;
7034 
7035                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
7036                     m=(ssize_t) mng_info->magn_mb;
7037 
7038                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
7039                     m=(ssize_t) mng_info->magn_mb;
7040 
7041                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
7042                     m=1;
7043 
7044                   else
7045                     m=(ssize_t) mng_info->magn_my;
7046 
7047                   n=prev;
7048                   prev=next;
7049                   next=n;
7050 
7051                   if (y < (ssize_t) image->rows-1)
7052                     {
7053                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
7054                           exception);
7055                       (void) memcpy(next,n,length);
7056                     }
7057 
7058                   for (i=0; i < m; i++, yy++)
7059                   {
7060                     register Quantum
7061                       *pixels;
7062 
7063                     assert(yy < (ssize_t) large_image->rows);
7064                     pixels=prev;
7065                     n=next;
7066                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7067                       1,exception);
7068                     if (q == (Quantum *) NULL)
7069                       break;
7070                     q+=(large_image->columns-image->columns)*
7071                       GetPixelChannels(large_image);
7072 
7073                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
7074                     {
7075                       /* To do: get color as function of indexes[x] */
7076                       /*
7077                       if (image->storage_class == PseudoClass)
7078                         {
7079                         }
7080                       */
7081 
7082                       if (magn_methy <= 1)
7083                         {
7084                           /* replicate previous */
7085                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7086                           SetPixelGreen(large_image,GetPixelGreen(image,
7087                              pixels),q);
7088                           SetPixelBlue(large_image,GetPixelBlue(image,
7089                              pixels),q);
7090                           SetPixelAlpha(large_image,GetPixelAlpha(image,
7091                              pixels),q);
7092                         }
7093 
7094                       else if (magn_methy == 2 || magn_methy == 4)
7095                         {
7096                           if (i == 0)
7097                             {
7098                               SetPixelRed(large_image,GetPixelRed(image,
7099                                  pixels),q);
7100                               SetPixelGreen(large_image,GetPixelGreen(image,
7101                                  pixels),q);
7102                               SetPixelBlue(large_image,GetPixelBlue(image,
7103                                  pixels),q);
7104                               SetPixelAlpha(large_image,GetPixelAlpha(image,
7105                                  pixels),q);
7106                             }
7107 
7108                           else
7109                             {
7110                               /* Interpolate */
7111                               SetPixelRed(large_image,((QM) (((ssize_t)
7112                                  (2*i*(GetPixelRed(image,n)
7113                                  -GetPixelRed(image,pixels)+m))/
7114                                  ((ssize_t) (m*2))
7115                                  +GetPixelRed(image,pixels)))),q);
7116                               SetPixelGreen(large_image,((QM) (((ssize_t)
7117                                  (2*i*(GetPixelGreen(image,n)
7118                                  -GetPixelGreen(image,pixels)+m))/
7119                                  ((ssize_t) (m*2))
7120                                  +GetPixelGreen(image,pixels)))),q);
7121                               SetPixelBlue(large_image,((QM) (((ssize_t)
7122                                  (2*i*(GetPixelBlue(image,n)
7123                                  -GetPixelBlue(image,pixels)+m))/
7124                                  ((ssize_t) (m*2))
7125                                  +GetPixelBlue(image,pixels)))),q);
7126 
7127                               if (image->alpha_trait != UndefinedPixelTrait)
7128                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
7129                                     (2*i*(GetPixelAlpha(image,n)
7130                                     -GetPixelAlpha(image,pixels)+m))
7131                                     /((ssize_t) (m*2))+
7132                                    GetPixelAlpha(image,pixels)))),q);
7133                             }
7134 
7135                           if (magn_methy == 4)
7136                             {
7137                               /* Replicate nearest */
7138                               if (i <= ((m+1) << 1))
7139                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7140                                     pixels),q);
7141                               else
7142                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7143                                     n),q);
7144                             }
7145                         }
7146 
7147                       else /* if (magn_methy == 3 || magn_methy == 5) */
7148                         {
7149                           /* Replicate nearest */
7150                           if (i <= ((m+1) << 1))
7151                           {
7152                              SetPixelRed(large_image,GetPixelRed(image,
7153                                     pixels),q);
7154                              SetPixelGreen(large_image,GetPixelGreen(image,
7155                                     pixels),q);
7156                              SetPixelBlue(large_image,GetPixelBlue(image,
7157                                     pixels),q);
7158                              SetPixelAlpha(large_image,GetPixelAlpha(image,
7159                                     pixels),q);
7160                           }
7161 
7162                           else
7163                           {
7164                              SetPixelRed(large_image,GetPixelRed(image,n),q);
7165                              SetPixelGreen(large_image,GetPixelGreen(image,n),
7166                                     q);
7167                              SetPixelBlue(large_image,GetPixelBlue(image,n),
7168                                     q);
7169                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7170                                     q);
7171                           }
7172 
7173                           if (magn_methy == 5)
7174                             {
7175                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7176                                  (GetPixelAlpha(image,n)
7177                                  -GetPixelAlpha(image,pixels))
7178                                  +m))/((ssize_t) (m*2))
7179                                  +GetPixelAlpha(image,pixels)),q);
7180                             }
7181                         }
7182                       n+=GetPixelChannels(image);
7183                       q+=GetPixelChannels(large_image);
7184                       pixels+=GetPixelChannels(image);
7185                     } /* x */
7186 
7187                     if (SyncAuthenticPixels(large_image,exception) == 0)
7188                       break;
7189 
7190                   } /* i */
7191                 } /* y */
7192 
7193                 prev=(Quantum *) RelinquishMagickMemory(prev);
7194                 next=(Quantum *) RelinquishMagickMemory(next);
7195 
7196                 length=image->columns;
7197 
7198                 if (logging != MagickFalse)
7199                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7200                     "    Delete original image");
7201 
7202                 DeleteImageFromList(&image);
7203 
7204                 image=large_image;
7205 
7206                 mng_info->image=image;
7207 
7208                 /* magnify the columns */
7209                 if (logging != MagickFalse)
7210                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7211                     "    Magnify the columns to %.20g",
7212                     (double) image->columns);
7213 
7214                 for (y=0; y < (ssize_t) image->rows; y++)
7215                 {
7216                   register Quantum
7217                     *pixels;
7218 
7219                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7220                   if (q == (Quantum *) NULL)
7221                     break;
7222                   pixels=q+(image->columns-length)*GetPixelChannels(image);
7223                   n=pixels+GetPixelChannels(image);
7224 
7225                   for (x=(ssize_t) (image->columns-length);
7226                     x < (ssize_t) image->columns; x++)
7227                   {
7228                     /* To do: Rewrite using Get/Set***PixelChannel() */
7229 
7230                     if (x == (ssize_t) (image->columns-length))
7231                       m=(ssize_t) mng_info->magn_ml;
7232 
7233                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7234                       m=(ssize_t) mng_info->magn_mr;
7235 
7236                     else if (magn_methx <= 1 &&
7237                         x == (ssize_t) image->columns-1)
7238                       m=(ssize_t) mng_info->magn_mr;
7239 
7240                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7241                       m=1;
7242 
7243                     else
7244                       m=(ssize_t) mng_info->magn_mx;
7245 
7246                     for (i=0; i < m; i++)
7247                     {
7248                       if (magn_methx <= 1)
7249                         {
7250                           /* replicate previous */
7251                           SetPixelRed(image,GetPixelRed(image,pixels),q);
7252                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7253                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7254                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7255                         }
7256 
7257                       else if (magn_methx == 2 || magn_methx == 4)
7258                         {
7259                           if (i == 0)
7260                           {
7261                             SetPixelRed(image,GetPixelRed(image,pixels),q);
7262                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7263                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7264                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7265                           }
7266 
7267                           /* To do: Rewrite using Get/Set***PixelChannel() */
7268                           else
7269                             {
7270                               /* Interpolate */
7271                               SetPixelRed(image,(QM) ((2*i*(
7272                                  GetPixelRed(image,n)
7273                                  -GetPixelRed(image,pixels))+m)
7274                                  /((ssize_t) (m*2))+
7275                                  GetPixelRed(image,pixels)),q);
7276 
7277                               SetPixelGreen(image,(QM) ((2*i*(
7278                                  GetPixelGreen(image,n)
7279                                  -GetPixelGreen(image,pixels))+m)
7280                                  /((ssize_t) (m*2))+
7281                                  GetPixelGreen(image,pixels)),q);
7282 
7283                               SetPixelBlue(image,(QM) ((2*i*(
7284                                  GetPixelBlue(image,n)
7285                                  -GetPixelBlue(image,pixels))+m)
7286                                  /((ssize_t) (m*2))+
7287                                  GetPixelBlue(image,pixels)),q);
7288                               if (image->alpha_trait != UndefinedPixelTrait)
7289                                  SetPixelAlpha(image,(QM) ((2*i*(
7290                                    GetPixelAlpha(image,n)
7291                                    -GetPixelAlpha(image,pixels))+m)
7292                                    /((ssize_t) (m*2))+
7293                                    GetPixelAlpha(image,pixels)),q);
7294                             }
7295 
7296                           if (magn_methx == 4)
7297                             {
7298                               /* Replicate nearest */
7299                               if (i <= ((m+1) << 1))
7300                               {
7301                                  SetPixelAlpha(image,
7302                                    GetPixelAlpha(image,pixels)+0,q);
7303                               }
7304                               else
7305                               {
7306                                  SetPixelAlpha(image,
7307                                    GetPixelAlpha(image,n)+0,q);
7308                               }
7309                             }
7310                         }
7311 
7312                       else /* if (magn_methx == 3 || magn_methx == 5) */
7313                         {
7314                           /* Replicate nearest */
7315                           if (i <= ((m+1) << 1))
7316                           {
7317                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7318                              SetPixelGreen(image,GetPixelGreen(image,
7319                                  pixels),q);
7320                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7321                              SetPixelAlpha(image,GetPixelAlpha(image,
7322                                  pixels),q);
7323                           }
7324 
7325                           else
7326                           {
7327                              SetPixelRed(image,GetPixelRed(image,n),q);
7328                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7329                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7330                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7331                           }
7332 
7333                           if (magn_methx == 5)
7334                             {
7335                               /* Interpolate */
7336                               SetPixelAlpha(image,
7337                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7338                                  -GetPixelAlpha(image,pixels))+m)/
7339                                  ((ssize_t) (m*2))
7340                                  +GetPixelAlpha(image,pixels)),q);
7341                             }
7342                         }
7343                       q+=GetPixelChannels(image);
7344                     }
7345                     n+=GetPixelChannels(image);
7346                   }
7347 
7348                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7349                     break;
7350                 }
7351 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7352               if (magn_methx != 1 || magn_methy != 1)
7353                 {
7354                 /*
7355                    Rescale pixels to Quantum
7356                 */
7357                    for (y=0; y < (ssize_t) image->rows; y++)
7358                    {
7359                      q=GetAuthenticPixels(image,0,y,image->columns,1,
7360                        exception);
7361                      if (q == (Quantum *) NULL)
7362                        break;
7363 
7364                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7365                      {
7366                         SetPixelRed(image,ScaleShortToQuantum(
7367                           GetPixelRed(image,q)),q);
7368                         SetPixelGreen(image,ScaleShortToQuantum(
7369                           GetPixelGreen(image,q)),q);
7370                         SetPixelBlue(image,ScaleShortToQuantum(
7371                           GetPixelBlue(image,q)),q);
7372                         SetPixelAlpha(image,ScaleShortToQuantum(
7373                           GetPixelAlpha(image,q)),q);
7374                         q+=GetPixelChannels(image);
7375                      }
7376 
7377                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7378                        break;
7379                    }
7380                 }
7381 #endif
7382                 if (logging != MagickFalse)
7383                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7384                     "  Finished MAGN processing");
7385               }
7386           }
7387 
7388         /*
7389           Crop_box is with respect to the upper left corner of the MNG.
7390         */
7391         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7392         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7393         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7394         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7395         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7396         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7397         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7398         if ((crop_box.left != (mng_info->image_box.left
7399             +mng_info->x_off[object_id])) ||
7400             (crop_box.right != (mng_info->image_box.right
7401             +mng_info->x_off[object_id])) ||
7402             (crop_box.top != (mng_info->image_box.top
7403             +mng_info->y_off[object_id])) ||
7404             (crop_box.bottom != (mng_info->image_box.bottom
7405             +mng_info->y_off[object_id])))
7406           {
7407             if (logging != MagickFalse)
7408               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7409                 "  Crop the PNG image");
7410 
7411             if ((crop_box.left < crop_box.right) &&
7412                 (crop_box.top < crop_box.bottom))
7413               {
7414                 Image
7415                   *im;
7416 
7417                 RectangleInfo
7418                   crop_info;
7419 
7420                 /*
7421                   Crop_info is with respect to the upper left corner of
7422                   the image.
7423                 */
7424                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7425                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7426                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7427                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7428                 image->page.width=image->columns;
7429                 image->page.height=image->rows;
7430                 image->page.x=0;
7431                 image->page.y=0;
7432                 im=CropImage(image,&crop_info,exception);
7433 
7434                 if (im != (Image *) NULL)
7435                   {
7436                     image->columns=im->columns;
7437                     image->rows=im->rows;
7438                     im=DestroyImage(im);
7439                     image->page.width=image->columns;
7440                     image->page.height=image->rows;
7441                     image->page.x=crop_box.left;
7442                     image->page.y=crop_box.top;
7443                   }
7444               }
7445 
7446             else
7447               {
7448                 /*
7449                   No pixels in crop area.  The MNG spec still requires
7450                   a layer, though, so make a single transparent pixel in
7451                   the top left corner.
7452                 */
7453                 image->columns=1;
7454                 image->rows=1;
7455                 image->colors=2;
7456                 (void) SetImageBackgroundColor(image,exception);
7457                 image->page.width=1;
7458                 image->page.height=1;
7459                 image->page.x=0;
7460                 image->page.y=0;
7461               }
7462           }
7463 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7464         image=mng_info->image;
7465 #endif
7466       }
7467 
7468 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7469       /* PNG does not handle depths greater than 16 so reduce it even
7470        * if lossy.
7471        */
7472       if (image->depth > 16)
7473          image->depth=16;
7474 #endif
7475 
7476 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7477       if (image->depth > 8)
7478         {
7479           /* To do: fill low byte properly */
7480           image->depth=16;
7481         }
7482 
7483       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7484          image->depth = 8;
7485 #endif
7486 
7487       if (image_info->number_scenes != 0)
7488         {
7489           if (mng_info->scenes_found >
7490              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7491             break;
7492         }
7493 
7494       if (logging != MagickFalse)
7495         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7496           "  Finished reading image datastream.");
7497 
7498   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7499 
7500   (void) CloseBlob(image);
7501 
7502   if (logging != MagickFalse)
7503     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7504       "  Finished reading all image datastreams.");
7505 
7506 #if defined(MNG_INSERT_LAYERS)
7507   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7508        (mng_info->mng_height))
7509     {
7510       /*
7511         Insert a background layer if nothing else was found.
7512       */
7513       if (logging != MagickFalse)
7514         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7515           "  No images found.  Inserting a background layer.");
7516 
7517       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7518         {
7519           /*
7520             Allocate next image structure.
7521           */
7522           AcquireNextImage(image_info,image,exception);
7523           if (GetNextImageInList(image) == (Image *) NULL)
7524             {
7525               if (logging != MagickFalse)
7526                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7527                   "  Allocation failed, returning NULL.");
7528 
7529               return(DestroyImageList(image));;
7530             }
7531           image=SyncNextImageInList(image);
7532         }
7533       image->columns=mng_info->mng_width;
7534       image->rows=mng_info->mng_height;
7535       image->page.width=mng_info->mng_width;
7536       image->page.height=mng_info->mng_height;
7537       image->page.x=0;
7538       image->page.y=0;
7539       image->background_color=mng_background_color;
7540       image->alpha_trait=UndefinedPixelTrait;
7541 
7542       if (image_info->ping == MagickFalse)
7543         (void) SetImageBackgroundColor(image,exception);
7544 
7545       mng_info->image_found++;
7546     }
7547 #endif
7548   image->iterations=mng_iterations;
7549 
7550   if (mng_iterations == 1)
7551     image->start_loop=MagickTrue;
7552 
7553   while (GetPreviousImageInList(image) != (Image *) NULL)
7554   {
7555     image_count++;
7556     if (image_count > 10*mng_info->image_found)
7557       {
7558         if (logging != MagickFalse)
7559           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7560 
7561         (void) ThrowMagickException(exception,GetMagickModule(),
7562           CoderError,"Linked list is corrupted, beginning of list not found",
7563           "`%s'",image_info->filename);
7564 
7565         return(DestroyImageList(image));
7566       }
7567 
7568     image=GetPreviousImageInList(image);
7569 
7570     if (GetNextImageInList(image) == (Image *) NULL)
7571       {
7572         if (logging != MagickFalse)
7573           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7574 
7575         (void) ThrowMagickException(exception,GetMagickModule(),
7576           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7577           image_info->filename);
7578       }
7579   }
7580 
7581   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7582              GetNextImageInList(image) ==
7583      (Image *) NULL)
7584     {
7585       if (logging != MagickFalse)
7586         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7587             "  First image null");
7588 
7589       (void) ThrowMagickException(exception,GetMagickModule(),
7590         CoderError,"image->next for first image is NULL but shouldn't be.",
7591         "`%s'",image_info->filename);
7592     }
7593 
7594   if (mng_info->image_found == 0)
7595     {
7596       if (logging != MagickFalse)
7597         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7598           "  No visible images found.");
7599 
7600       (void) ThrowMagickException(exception,GetMagickModule(),
7601         CoderError,"No visible images in file","`%s'",image_info->filename);
7602 
7603       return(DestroyImageList(image));
7604     }
7605 
7606   if (mng_info->ticks_per_second)
7607     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7608             final_delay/mng_info->ticks_per_second;
7609 
7610   else
7611     image->start_loop=MagickTrue;
7612 
7613   /* Find final nonzero image delay */
7614   final_image_delay=0;
7615 
7616   while (GetNextImageInList(image) != (Image *) NULL)
7617     {
7618       if (image->delay)
7619         final_image_delay=image->delay;
7620 
7621       image=GetNextImageInList(image);
7622     }
7623 
7624   if (final_delay < final_image_delay)
7625     final_delay=final_image_delay;
7626 
7627   image->delay=final_delay;
7628 
7629   if (logging != MagickFalse)
7630       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7631         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7632         (double) final_delay);
7633 
7634   if (logging != MagickFalse)
7635     {
7636       int
7637         scene;
7638 
7639       scene=0;
7640       image=GetFirstImageInList(image);
7641 
7642       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7643         "  Before coalesce:");
7644 
7645       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7646         "    scene 0 delay=%.20g",(double) image->delay);
7647 
7648       while (GetNextImageInList(image) != (Image *) NULL)
7649       {
7650         image=GetNextImageInList(image);
7651         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7652           "    scene %.20g delay=%.20g",(double) scene++,
7653           (double) image->delay);
7654       }
7655     }
7656 
7657   image=GetFirstImageInList(image);
7658 #ifdef MNG_COALESCE_LAYERS
7659   if (insert_layers)
7660     {
7661       Image
7662         *next_image,
7663         *next;
7664 
7665       size_t
7666         scene;
7667 
7668       if (logging != MagickFalse)
7669         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7670           "  Coalesce Images");
7671 
7672       scene=image->scene;
7673       next_image=CoalesceImages(image,exception);
7674 
7675       if (next_image == (Image *) NULL)
7676         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7677 
7678       image=DestroyImageList(image);
7679       image=next_image;
7680 
7681       for (next=image; next != (Image *) NULL; next=next_image)
7682       {
7683          next->page.width=mng_info->mng_width;
7684          next->page.height=mng_info->mng_height;
7685          next->page.x=0;
7686          next->page.y=0;
7687          next->scene=scene++;
7688          next_image=GetNextImageInList(next);
7689 
7690          if (next_image == (Image *) NULL)
7691            break;
7692 
7693          if (next->delay == 0)
7694            {
7695              scene--;
7696              next_image->previous=GetPreviousImageInList(next);
7697              if (GetPreviousImageInList(next) == (Image *) NULL)
7698                image=next_image;
7699              else
7700                next->previous->next=next_image;
7701              next=DestroyImage(next);
7702            }
7703       }
7704     }
7705 #endif
7706 
7707   while (GetNextImageInList(image) != (Image *) NULL)
7708       image=GetNextImageInList(image);
7709 
7710   image->dispose=BackgroundDispose;
7711 
7712   if (logging != MagickFalse)
7713     {
7714       int
7715         scene;
7716 
7717       scene=0;
7718       image=GetFirstImageInList(image);
7719 
7720       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7721         "  After coalesce:");
7722 
7723       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7724         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7725         (double) image->dispose);
7726 
7727       while (GetNextImageInList(image) != (Image *) NULL)
7728       {
7729         image=GetNextImageInList(image);
7730 
7731         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7732           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7733           (double) image->delay,(double) image->dispose);
7734       }
7735    }
7736 
7737   if (logging != MagickFalse)
7738     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7739       "  exit ReadOneMNGImage();");
7740 
7741   return(image);
7742 }
7743 
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7744 static Image *ReadMNGImage(const ImageInfo *image_info,
7745      ExceptionInfo *exception)
7746 {
7747   Image
7748     *image;
7749 
7750   MagickBooleanType
7751     logging,
7752     status;
7753 
7754   MngInfo
7755     *mng_info;
7756 
7757   /* Open image file.  */
7758 
7759   assert(image_info != (const ImageInfo *) NULL);
7760   assert(image_info->signature == MagickCoreSignature);
7761   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7762      image_info->filename);
7763   assert(exception != (ExceptionInfo *) NULL);
7764   assert(exception->signature == MagickCoreSignature);
7765   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7766   image=AcquireImage(image_info,exception);
7767   mng_info=(MngInfo *) NULL;
7768   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7769 
7770   if (status == MagickFalse)
7771     return(DestroyImageList(image));
7772 
7773   /* Allocate a MngInfo structure.  */
7774 
7775   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7776 
7777   if (mng_info == (MngInfo *) NULL)
7778     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7779 
7780   /* Initialize members of the MngInfo structure.  */
7781 
7782   (void) memset(mng_info,0,sizeof(MngInfo));
7783   mng_info->image=image;
7784   image=ReadOneMNGImage(mng_info,image_info,exception);
7785   mng_info=MngInfoFreeStruct(mng_info);
7786 
7787   if (image == (Image *) NULL)
7788     {
7789       if (logging != MagickFalse)
7790         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7791           "exit ReadMNGImage() with error");
7792 
7793       return((Image *) NULL);
7794     }
7795   (void) CloseBlob(image);
7796 
7797   if (logging != MagickFalse)
7798     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7799 
7800   return(GetFirstImageInList(image));
7801 }
7802 #else /* PNG_LIBPNG_VER > 10011 */
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7803 static Image *ReadPNGImage(const ImageInfo *image_info,
7804    ExceptionInfo *exception)
7805 {
7806   printf("Your PNG library is too old: You have libpng-%s\n",
7807      PNG_LIBPNG_VER_STRING);
7808 
7809   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7810     "PNG library is too old","`%s'",image_info->filename);
7811 
7812   return(Image *) NULL;
7813 }
7814 
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7815 static Image *ReadMNGImage(const ImageInfo *image_info,
7816    ExceptionInfo *exception)
7817 {
7818   return(ReadPNGImage(image_info,exception));
7819 }
7820 #endif /* PNG_LIBPNG_VER > 10011 */
7821 #endif
7822 
7823 /*
7824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7825 %                                                                             %
7826 %                                                                             %
7827 %                                                                             %
7828 %   R e g i s t e r P N G I m a g e                                           %
7829 %                                                                             %
7830 %                                                                             %
7831 %                                                                             %
7832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7833 %
7834 %  RegisterPNGImage() adds properties for the PNG image format to
7835 %  the list of supported formats.  The properties include the image format
7836 %  tag, a method to read and/or write the format, whether the format
7837 %  supports the saving of more than one frame to the same file or blob,
7838 %  whether the format supports native in-memory I/O, and a brief
7839 %  description of the format.
7840 %
7841 %  The format of the RegisterPNGImage method is:
7842 %
7843 %      size_t RegisterPNGImage(void)
7844 %
7845 */
RegisterPNGImage(void)7846 ModuleExport size_t RegisterPNGImage(void)
7847 {
7848   char
7849     version[MagickPathExtent];
7850 
7851   MagickInfo
7852     *entry;
7853 
7854   static const char
7855     *PNGNote=
7856     {
7857       "See http://www.libpng.org/ for details about the PNG format."
7858     },
7859 
7860     *JNGNote=
7861     {
7862       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7863       "format."
7864     },
7865 
7866     *MNGNote=
7867     {
7868       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7869       "format."
7870     };
7871 
7872   *version='\0';
7873 
7874 #if defined(PNG_LIBPNG_VER_STRING)
7875   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7876   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7877    MagickPathExtent);
7878 
7879   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7880     {
7881       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7882       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7883             MagickPathExtent);
7884     }
7885 #endif
7886 
7887   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7888   entry->flags|=CoderDecoderSeekableStreamFlag;
7889 
7890 #if defined(MAGICKCORE_PNG_DELEGATE)
7891   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7892   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7893 #endif
7894 
7895   entry->magick=(IsImageFormatHandler *) IsMNG;
7896 
7897   if (*version != '\0')
7898     entry->version=ConstantString(version);
7899 
7900   entry->mime_type=ConstantString("video/x-mng");
7901   entry->note=ConstantString(MNGNote);
7902   (void) RegisterMagickInfo(entry);
7903 
7904   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7905 
7906 #if defined(MAGICKCORE_PNG_DELEGATE)
7907   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7908   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7909 #endif
7910 
7911   entry->magick=(IsImageFormatHandler *) IsPNG;
7912   entry->flags|=CoderDecoderSeekableStreamFlag;
7913   entry->flags^=CoderAdjoinFlag;
7914   entry->mime_type=ConstantString("image/png");
7915 
7916   if (*version != '\0')
7917     entry->version=ConstantString(version);
7918 
7919   entry->note=ConstantString(PNGNote);
7920   (void) RegisterMagickInfo(entry);
7921 
7922   entry=AcquireMagickInfo("PNG","PNG8",
7923     "8-bit indexed with optional binary transparency");
7924 
7925 #if defined(MAGICKCORE_PNG_DELEGATE)
7926   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7927   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7928 #endif
7929 
7930   entry->magick=(IsImageFormatHandler *) IsPNG;
7931   entry->flags|=CoderDecoderSeekableStreamFlag;
7932   entry->flags^=CoderAdjoinFlag;
7933   entry->mime_type=ConstantString("image/png");
7934   (void) RegisterMagickInfo(entry);
7935 
7936   entry=AcquireMagickInfo("PNG","PNG24",
7937     "opaque or binary transparent 24-bit RGB");
7938   *version='\0';
7939 
7940 #if defined(ZLIB_VERSION)
7941   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7942   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7943 
7944   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7945     {
7946       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7947       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7948     }
7949 #endif
7950 
7951   if (*version != '\0')
7952     entry->version=ConstantString(version);
7953 
7954 #if defined(MAGICKCORE_PNG_DELEGATE)
7955   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7956   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7957 #endif
7958 
7959   entry->magick=(IsImageFormatHandler *) IsPNG;
7960   entry->flags|=CoderDecoderSeekableStreamFlag;
7961   entry->flags^=CoderAdjoinFlag;
7962   entry->mime_type=ConstantString("image/png");
7963   (void) RegisterMagickInfo(entry);
7964 
7965   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7966 
7967 #if defined(MAGICKCORE_PNG_DELEGATE)
7968   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7969   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7970 #endif
7971 
7972   entry->magick=(IsImageFormatHandler *) IsPNG;
7973   entry->flags|=CoderDecoderSeekableStreamFlag;
7974   entry->flags^=CoderAdjoinFlag;
7975   entry->mime_type=ConstantString("image/png");
7976   (void) RegisterMagickInfo(entry);
7977 
7978   entry=AcquireMagickInfo("PNG","PNG48",
7979     "opaque or binary transparent 48-bit RGB");
7980 
7981 #if defined(MAGICKCORE_PNG_DELEGATE)
7982   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7983   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7984 #endif
7985 
7986   entry->magick=(IsImageFormatHandler *) IsPNG;
7987   entry->flags|=CoderDecoderSeekableStreamFlag;
7988   entry->flags^=CoderAdjoinFlag;
7989   entry->mime_type=ConstantString("image/png");
7990   (void) RegisterMagickInfo(entry);
7991 
7992   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7993 
7994 #if defined(MAGICKCORE_PNG_DELEGATE)
7995   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7996   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7997 #endif
7998 
7999   entry->magick=(IsImageFormatHandler *) IsPNG;
8000   entry->flags|=CoderDecoderSeekableStreamFlag;
8001   entry->flags^=CoderAdjoinFlag;
8002   entry->mime_type=ConstantString("image/png");
8003   (void) RegisterMagickInfo(entry);
8004 
8005   entry=AcquireMagickInfo("PNG","PNG00",
8006     "PNG inheriting bit-depth, color-type from original, if possible");
8007 
8008 #if defined(MAGICKCORE_PNG_DELEGATE)
8009   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
8010   entry->encoder=(EncodeImageHandler *) WritePNGImage;
8011 #endif
8012 
8013   entry->magick=(IsImageFormatHandler *) IsPNG;
8014   entry->flags|=CoderDecoderSeekableStreamFlag;
8015   entry->flags^=CoderAdjoinFlag;
8016   entry->mime_type=ConstantString("image/png");
8017   (void) RegisterMagickInfo(entry);
8018 
8019   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
8020 
8021 #if defined(JNG_SUPPORTED)
8022 #if defined(MAGICKCORE_PNG_DELEGATE)
8023   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
8024   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
8025 #endif
8026 #endif
8027 
8028   entry->magick=(IsImageFormatHandler *) IsJNG;
8029   entry->flags|=CoderDecoderSeekableStreamFlag;
8030   entry->flags^=CoderAdjoinFlag;
8031   entry->mime_type=ConstantString("image/x-jng");
8032   entry->note=ConstantString(JNGNote);
8033   (void) RegisterMagickInfo(entry);
8034 
8035 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8036   ping_semaphore=AcquireSemaphoreInfo();
8037 #endif
8038 
8039   return(MagickImageCoderSignature);
8040 }
8041 
8042 /*
8043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8044 %                                                                             %
8045 %                                                                             %
8046 %                                                                             %
8047 %   U n r e g i s t e r P N G I m a g e                                       %
8048 %                                                                             %
8049 %                                                                             %
8050 %                                                                             %
8051 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8052 %
8053 %  UnregisterPNGImage() removes format registrations made by the
8054 %  PNG module from the list of supported formats.
8055 %
8056 %  The format of the UnregisterPNGImage method is:
8057 %
8058 %      UnregisterPNGImage(void)
8059 %
8060 */
UnregisterPNGImage(void)8061 ModuleExport void UnregisterPNGImage(void)
8062 {
8063   (void) UnregisterMagickInfo("MNG");
8064   (void) UnregisterMagickInfo("PNG");
8065   (void) UnregisterMagickInfo("PNG8");
8066   (void) UnregisterMagickInfo("PNG24");
8067   (void) UnregisterMagickInfo("PNG32");
8068   (void) UnregisterMagickInfo("PNG48");
8069   (void) UnregisterMagickInfo("PNG64");
8070   (void) UnregisterMagickInfo("PNG00");
8071   (void) UnregisterMagickInfo("JNG");
8072 
8073 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8074   if (ping_semaphore != (SemaphoreInfo *) NULL)
8075     RelinquishSemaphoreInfo(&ping_semaphore);
8076 #endif
8077 }
8078 
8079 #if defined(MAGICKCORE_PNG_DELEGATE)
8080 #if PNG_LIBPNG_VER > 10011
8081 /*
8082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8083 %                                                                             %
8084 %                                                                             %
8085 %                                                                             %
8086 %   W r i t e M N G I m a g e                                                 %
8087 %                                                                             %
8088 %                                                                             %
8089 %                                                                             %
8090 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8091 %
8092 %  WriteMNGImage() writes an image in the Portable Network Graphics
8093 %  Group's "Multiple-image Network Graphics" encoded image format.
8094 %
8095 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
8096 %
8097 %  The format of the WriteMNGImage method is:
8098 %
8099 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8100 %        Image *image,ExceptionInfo *exception)
8101 %
8102 %  A description of each parameter follows.
8103 %
8104 %    o image_info: the image info.
8105 %
8106 %    o image:  The image.
8107 %
8108 %    o exception: return any errors or warnings in this structure.
8109 %
8110 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8111 %    "To do" under ReadPNGImage):
8112 %
8113 %    Preserve all unknown and not-yet-handled known chunks found in input
8114 %    PNG file and copy them  into output PNG files according to the PNG
8115 %    copying rules.
8116 %
8117 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
8118 %
8119 %    Improve selection of color type (use indexed-colour or indexed-colour
8120 %    with tRNS when 256 or fewer unique RGBA values are present).
8121 %
8122 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8123 %    This will be complicated if we limit ourselves to generating MNG-LC
8124 %    files.  For now we ignore disposal method 3 and simply overlay the next
8125 %    image on it.
8126 %
8127 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
8128 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
8129 %    [mostly done 15 June 1999 but still need to take care of tRNS]
8130 %
8131 %    Check for identical sRGB and replace with a global sRGB (and remove
8132 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8133 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8134 %    local gAMA/cHRM with local sRGB if appropriate).
8135 %
8136 %    Check for identical sBIT chunks and write global ones.
8137 %
8138 %    Provide option to skip writing the signature tEXt chunks.
8139 %
8140 %    Use signatures to detect identical objects and reuse the first
8141 %    instance of such objects instead of writing duplicate objects.
8142 %
8143 %    Use a smaller-than-32k value of compression window size when
8144 %    appropriate.
8145 %
8146 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
8147 %    ancillary text chunks and save profiles.
8148 %
8149 %    Provide an option to force LC files (to ensure exact framing rate)
8150 %    instead of VLC.
8151 %
8152 %    Provide an option to force VLC files instead of LC, even when offsets
8153 %    are present.  This will involve expanding the embedded images with a
8154 %    transparent region at the top and/or left.
8155 */
8156 
8157 static void
Magick_png_write_raw_profile(const ImageInfo * image_info,png_struct * ping,png_info * ping_info,unsigned char * profile_type,unsigned char * profile_description,unsigned char * profile_data,png_uint_32 length)8158 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8159    png_info *ping_info, unsigned char *profile_type, unsigned char
8160    *profile_description, unsigned char *profile_data, png_uint_32 length)
8161 {
8162    png_textp
8163      text;
8164 
8165    register ssize_t
8166      i;
8167 
8168    unsigned char
8169      *sp;
8170 
8171    png_charp
8172      dp;
8173 
8174    png_uint_32
8175      allocated_length,
8176      description_length;
8177 
8178    unsigned char
8179      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8180 
8181    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8182       return;
8183 
8184    if (image_info->verbose)
8185      {
8186        (void) printf("writing raw profile: type=%s, length=%.20g\n",
8187          (char *) profile_type, (double) length);
8188      }
8189 
8190 #if PNG_LIBPNG_VER >= 10400
8191    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8192 #else
8193    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8194 #endif
8195    description_length=(png_uint_32) strlen((const char *) profile_description);
8196    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8197       + description_length);
8198 #if PNG_LIBPNG_VER >= 10400
8199    text[0].text=(png_charp) png_malloc(ping,
8200       (png_alloc_size_t) allocated_length);
8201    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8202 #else
8203    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8204    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8205 #endif
8206    text[0].key[0]='\0';
8207    (void) ConcatenateMagickString(text[0].key,
8208       "Raw profile type ",MagickPathExtent);
8209    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8210    sp=profile_data;
8211    dp=text[0].text;
8212    *dp++='\n';
8213    (void) CopyMagickString(dp,(const char *) profile_description,
8214      allocated_length);
8215    dp+=description_length;
8216    *dp++='\n';
8217    (void) FormatLocaleString(dp,allocated_length-
8218      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8219    dp+=8;
8220 
8221    for (i=0; i < (ssize_t) length; i++)
8222    {
8223      if (i%36 == 0)
8224        *dp++='\n';
8225      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8226      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8227    }
8228 
8229    *dp++='\n';
8230    *dp='\0';
8231    text[0].text_length=(png_size_t) (dp-text[0].text);
8232    text[0].compression=image_info->compression == NoCompression ||
8233      (image_info->compression == UndefinedCompression &&
8234      text[0].text_length < 128) ? -1 : 0;
8235 
8236    if (text[0].text_length <= allocated_length)
8237      png_set_text(ping,ping_info,text,1);
8238 
8239    png_free(ping,text[0].text);
8240    png_free(ping,text[0].key);
8241    png_free(ping,text);
8242 }
8243 
IsColorEqual(const Image * image,const Quantum * p,const PixelInfo * q)8244 static inline MagickBooleanType IsColorEqual(const Image *image,
8245   const Quantum *p, const PixelInfo *q)
8246 {
8247   MagickRealType
8248     blue,
8249     green,
8250     red;
8251 
8252   red=(MagickRealType) GetPixelRed(image,p);
8253   green=(MagickRealType) GetPixelGreen(image,p);
8254   blue=(MagickRealType) GetPixelBlue(image,p);
8255   if ((AbsolutePixelValue(red-q->red) < MagickEpsilon) &&
8256       (AbsolutePixelValue(green-q->green) < MagickEpsilon) &&
8257       (AbsolutePixelValue(blue-q->blue) < MagickEpsilon))
8258     return(MagickTrue);
8259   return(MagickFalse);
8260 }
8261 
8262 #if defined(PNG_tIME_SUPPORTED)
write_tIME_chunk(Image * image,png_struct * ping,png_info * info,const char * timestamp,ExceptionInfo * exception)8263 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8264   const char *timestamp,ExceptionInfo *exception)
8265 {
8266   int
8267     ret;
8268 
8269   int
8270     day,
8271     hour,
8272     minute,
8273     month,
8274     second,
8275     year;
8276 
8277   int
8278     addhours=0,
8279     addminutes=0;
8280 
8281   png_time
8282     ptime;
8283 
8284   assert(timestamp != (const char *) NULL);
8285   LogMagickEvent(CoderEvent,GetMagickModule(),
8286       "  Writing tIME chunk: timestamp property is %30s\n",timestamp);
8287   ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d",&year,&month,&day,&hour,
8288       &minute, &second);
8289   addhours=0;
8290   addminutes=0;
8291   ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d%d:%d",&year,&month,&day,&hour,
8292       &minute, &second, &addhours, &addminutes);
8293     LogMagickEvent(CoderEvent,GetMagickModule(),
8294       "   Date format specified for png:tIME=%s" ,timestamp);
8295     LogMagickEvent(CoderEvent,GetMagickModule(),
8296       "      ret=%d,y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, as=%d",
8297       ret,year,month,day,hour,minute,second,addhours,addminutes);
8298   if (ret < 6)
8299   {
8300     LogMagickEvent(CoderEvent,GetMagickModule(),
8301       "      Invalid date, ret=%d",ret);
8302     (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8303       "Invalid date format specified for png:tIME","`%s' (ret=%d)",
8304       image->filename,ret);
8305     return;
8306   }
8307   if (addhours < 0)
8308   {
8309     addhours+=24;
8310     addminutes=-addminutes;
8311     day--;
8312   }
8313   hour+=addhours;
8314   minute+=addminutes;
8315   if (day == 0)
8316   {
8317     month--;
8318     day=31;
8319     if(month == 2)
8320       day=28;
8321     else
8322     {
8323       if(month == 4 || month == 6 || month == 9 || month == 11)
8324         day=30;
8325       else
8326         day=31;
8327     }
8328   }
8329   if (month == 0)
8330   {
8331     month++;
8332     year--;
8333   }
8334   if (minute > 59)
8335   {
8336      hour++;
8337      minute-=60;
8338   }
8339   if (hour > 23)
8340   {
8341      day ++;
8342      hour -=24;
8343   }
8344   if (hour < 0)
8345   {
8346      day --;
8347      hour +=24;
8348   }
8349   /* To do: fix this for leap years */
8350   if (day > 31 || (month == 2 && day > 28) || ((month == 4 || month == 6 ||
8351       month == 9 || month == 11) && day > 30))
8352   {
8353      month++;
8354      day = 1;
8355   }
8356   if (month > 12)
8357   {
8358      year++;
8359      month=1;
8360   }
8361 
8362   ptime.year = year;
8363   ptime.month = month;
8364   ptime.day = day;
8365   ptime.hour = hour;
8366   ptime.minute = minute;
8367   ptime.second = second;
8368 
8369   LogMagickEvent(CoderEvent,GetMagickModule(),
8370       "      png_set_tIME: y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, am=%d",
8371       ptime.year, ptime.month, ptime.day, ptime.hour, ptime.minute,
8372       ptime.second, addhours, addminutes);
8373   png_set_tIME(ping,info,&ptime);
8374 }
8375 #endif
8376 
8377 /* Write one PNG image */
WriteOnePNGImage(MngInfo * mng_info,const ImageInfo * IMimage_info,Image * IMimage,ExceptionInfo * exception)8378 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8379   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8380 {
8381   char
8382     im_vers[32],
8383     libpng_runv[32],
8384     libpng_vers[32],
8385     zlib_runv[32],
8386     zlib_vers[32];
8387 
8388   Image
8389     *image;
8390 
8391   ImageInfo
8392     *image_info;
8393 
8394   char
8395     *name,
8396     s[2];
8397 
8398   const char
8399     *property,
8400     *value;
8401 
8402   const StringInfo
8403     *profile;
8404 
8405   int
8406     num_passes,
8407     pass,
8408     ping_wrote_caNv;
8409 
8410   png_byte
8411      ping_trans_alpha[256];
8412 
8413   png_color
8414      palette[257];
8415 
8416   png_color_16
8417     ping_background,
8418     ping_trans_color;
8419 
8420   png_info
8421     *ping_info;
8422 
8423   png_struct
8424     *ping;
8425 
8426   png_uint_32
8427     ping_height,
8428     ping_width;
8429 
8430   ssize_t
8431     y;
8432 
8433   MagickBooleanType
8434     image_matte,
8435     logging,
8436     matte,
8437 
8438     ping_have_blob,
8439     ping_have_cheap_transparency,
8440     ping_have_color,
8441     ping_have_non_bw,
8442     ping_have_PLTE,
8443     ping_have_bKGD,
8444     ping_have_eXIf,
8445     ping_have_iCCP,
8446     ping_have_pHYs,
8447     ping_have_sRGB,
8448     ping_have_tRNS,
8449 
8450     ping_exclude_bKGD,
8451     ping_exclude_cHRM,
8452     ping_exclude_date,
8453     /* ping_exclude_EXIF, */
8454     ping_exclude_eXIf,
8455     ping_exclude_gAMA,
8456     ping_exclude_iCCP,
8457     /* ping_exclude_iTXt, */
8458     ping_exclude_oFFs,
8459     ping_exclude_pHYs,
8460     ping_exclude_sRGB,
8461     ping_exclude_tEXt,
8462     ping_exclude_tIME,
8463     /* ping_exclude_tRNS, */
8464     ping_exclude_caNv,
8465     ping_exclude_zCCP, /* hex-encoded iCCP */
8466     ping_exclude_zTXt,
8467 
8468     ping_preserve_colormap,
8469     ping_preserve_iCCP,
8470     ping_need_colortype_warning,
8471 
8472     status,
8473     tried_332,
8474     tried_333,
8475     tried_444;
8476 
8477   MemoryInfo
8478     *volatile pixel_info;
8479 
8480   QuantumInfo
8481     *quantum_info;
8482 
8483   PNGErrorInfo
8484     error_info;
8485 
8486   register ssize_t
8487     i,
8488     x;
8489 
8490   unsigned char
8491     *ping_pixels;
8492 
8493   volatile int
8494     image_colors,
8495     ping_bit_depth,
8496     ping_color_type,
8497     ping_interlace_method,
8498     ping_compression_method,
8499     ping_filter_method,
8500     ping_num_trans;
8501 
8502   volatile size_t
8503     image_depth,
8504     old_bit_depth;
8505 
8506   size_t
8507     quality,
8508     rowbytes,
8509     save_image_depth;
8510 
8511   int
8512     j,
8513     number_colors,
8514     number_opaque,
8515     number_semitransparent,
8516     number_transparent,
8517     ping_pHYs_unit_type;
8518 
8519   png_uint_32
8520     ping_pHYs_x_resolution,
8521     ping_pHYs_y_resolution;
8522 
8523   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8524     "  Enter WriteOnePNGImage()");
8525 
8526   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8527   if (image == (Image *) NULL)
8528     return(MagickFalse);
8529   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8530 
8531   /* Define these outside of the following "if logging()" block so they will
8532    * show in debuggers.
8533    */
8534   *im_vers='\0';
8535   (void) ConcatenateMagickString(im_vers,
8536          MagickLibVersionText,MagickPathExtent);
8537   (void) ConcatenateMagickString(im_vers,
8538          MagickLibAddendum,MagickPathExtent);
8539 
8540   *libpng_vers='\0';
8541   (void) ConcatenateMagickString(libpng_vers,
8542          PNG_LIBPNG_VER_STRING,32);
8543   *libpng_runv='\0';
8544   (void) ConcatenateMagickString(libpng_runv,
8545          png_get_libpng_ver(NULL),32);
8546 
8547   *zlib_vers='\0';
8548   (void) ConcatenateMagickString(zlib_vers,
8549          ZLIB_VERSION,32);
8550   *zlib_runv='\0';
8551   (void) ConcatenateMagickString(zlib_runv,
8552          zlib_version,32);
8553 
8554   if (logging != MagickFalse)
8555     {
8556        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8557           "    IM version     = %s", im_vers);
8558        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8559           "    Libpng version = %s", libpng_vers);
8560        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8561        {
8562        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8563           "      running with   %s", libpng_runv);
8564        }
8565        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8566           "    Zlib version   = %s", zlib_vers);
8567        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8568        {
8569        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8570           "      running with   %s", zlib_runv);
8571        }
8572     }
8573 
8574   /* Initialize some stuff */
8575   ping_bit_depth=0,
8576   ping_color_type=0,
8577   ping_interlace_method=0,
8578   ping_compression_method=0,
8579   ping_filter_method=0,
8580   ping_num_trans = 0;
8581 
8582   ping_background.red = 0;
8583   ping_background.green = 0;
8584   ping_background.blue = 0;
8585   ping_background.gray = 0;
8586   ping_background.index = 0;
8587 
8588   ping_trans_color.red=0;
8589   ping_trans_color.green=0;
8590   ping_trans_color.blue=0;
8591   ping_trans_color.gray=0;
8592 
8593   ping_pHYs_unit_type = 0;
8594   ping_pHYs_x_resolution = 0;
8595   ping_pHYs_y_resolution = 0;
8596 
8597   ping_have_blob=MagickFalse;
8598   ping_have_cheap_transparency=MagickFalse;
8599   ping_have_color=MagickTrue;
8600   ping_have_non_bw=MagickTrue;
8601   ping_have_PLTE=MagickFalse;
8602   ping_have_bKGD=MagickFalse;
8603   ping_have_eXIf=MagickTrue;
8604   ping_have_iCCP=MagickFalse;
8605   ping_have_pHYs=MagickFalse;
8606   ping_have_sRGB=MagickFalse;
8607   ping_have_tRNS=MagickFalse;
8608 
8609   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8610   ping_exclude_caNv=mng_info->ping_exclude_caNv;
8611   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8612   ping_exclude_date=mng_info->ping_exclude_date;
8613   ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8614   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8615   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8616   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8617   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8618   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8619   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8620   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8621   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8622   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8623   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8624   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8625 
8626   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8627   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8628   ping_need_colortype_warning = MagickFalse;
8629 
8630   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8631    * i.e., eliminate the ICC profile and set image->rendering_intent.
8632    * Note that this will not involve any changes to the actual pixels
8633    * but merely passes information to applications that read the resulting
8634    * PNG image.
8635    *
8636    * To do: recognize other variants of the sRGB profile, using the CRC to
8637    * verify all recognized variants including the 7 already known.
8638    *
8639    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8640    *
8641    * Use something other than image->rendering_intent to record the fact
8642    * that the sRGB profile was found.
8643    *
8644    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8645    * profile.  Record the Blackpoint Compensation, if any.
8646    */
8647    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8648    {
8649       ResetImageProfileIterator(image);
8650       for (name=GetNextImageProfile(image); name != (char *) NULL; )
8651       {
8652         profile=GetImageProfile(image,name);
8653 
8654         if (profile != (StringInfo *) NULL)
8655           {
8656             if ((LocaleCompare(name,"ICC") == 0) ||
8657                 (LocaleCompare(name,"ICM") == 0))
8658 
8659              {
8660                  int
8661                    icheck,
8662                    got_crc=0;
8663 
8664 
8665                  png_uint_32
8666                    length,
8667                    profile_crc=0;
8668 
8669                  unsigned char
8670                    *data;
8671 
8672                  length=(png_uint_32) GetStringInfoLength(profile);
8673 
8674                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8675                  {
8676                    if (length == sRGB_info[icheck].len)
8677                    {
8678                      if (got_crc == 0)
8679                      {
8680                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8681                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8682                          (unsigned long) length);
8683 
8684                        data=GetStringInfoDatum(profile);
8685                        profile_crc=crc32(0,data,length);
8686 
8687                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8688                            "      with crc=%8x",(unsigned int) profile_crc);
8689                        got_crc++;
8690                      }
8691 
8692                      if (profile_crc == sRGB_info[icheck].crc)
8693                      {
8694                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8695                             "      It is sRGB with rendering intent = %s",
8696                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8697                              sRGB_info[icheck].intent));
8698                         if (image->rendering_intent==UndefinedIntent)
8699                         {
8700                           image->rendering_intent=
8701                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8702                              sRGB_info[icheck].intent);
8703                         }
8704                         ping_exclude_iCCP = MagickTrue;
8705                         ping_exclude_zCCP = MagickTrue;
8706                         ping_have_sRGB = MagickTrue;
8707                         break;
8708                      }
8709                    }
8710                  }
8711                  if (sRGB_info[icheck].len == 0)
8712                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8713                         "    Got %lu-byte ICC profile not recognized as sRGB",
8714                         (unsigned long) length);
8715               }
8716           }
8717         name=GetNextImageProfile(image);
8718       }
8719   }
8720 
8721   number_opaque = 0;
8722   number_semitransparent = 0;
8723   number_transparent = 0;
8724 
8725   if (logging != MagickFalse)
8726     {
8727       if (image->storage_class == UndefinedClass)
8728           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8729           "    image->storage_class=UndefinedClass");
8730       if (image->storage_class == DirectClass)
8731           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732           "    image->storage_class=DirectClass");
8733       if (image->storage_class == PseudoClass)
8734           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8735           "    image->storage_class=PseudoClass");
8736       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8737           "    image->taint=MagickTrue":
8738           "    image->taint=MagickFalse");
8739       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8740           "    image->gamma=%g", image->gamma);
8741     }
8742 
8743   if (image->storage_class == PseudoClass &&
8744      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8745      mng_info->write_png48 || mng_info->write_png64 ||
8746      (mng_info->write_png_colortype != 1 &&
8747      mng_info->write_png_colortype != 5)))
8748     {
8749       (void) SyncImage(image,exception);
8750       image->storage_class = DirectClass;
8751     }
8752 
8753   if (ping_preserve_colormap == MagickFalse)
8754     {
8755       if (image->storage_class != PseudoClass && image->colormap != NULL)
8756         {
8757           /* Free the bogus colormap; it can cause trouble later */
8758            if (logging != MagickFalse)
8759               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8760               "    Freeing bogus colormap");
8761            (void) RelinquishMagickMemory(image->colormap);
8762            image->colormap=NULL;
8763         }
8764     }
8765 
8766   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8767     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8768 
8769   /*
8770     Sometimes we get PseudoClass images whose RGB values don't match
8771     the colors in the colormap.  This code syncs the RGB values.
8772   */
8773   image->depth=GetImageQuantumDepth(image,MagickFalse);
8774   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8775      (void) SyncImage(image,exception);
8776 
8777 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8778   if (image->depth > 8)
8779     {
8780       if (logging != MagickFalse)
8781         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8782           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8783 
8784       image->depth=8;
8785     }
8786 #endif
8787 
8788   /* Respect the -depth option */
8789   if (image->depth < 4)
8790     {
8791        register Quantum
8792          *r;
8793 
8794        if (image->depth > 2)
8795          {
8796            /* Scale to 4-bit */
8797            LBR04PacketRGBA(image->background_color);
8798 
8799            for (y=0; y < (ssize_t) image->rows; y++)
8800            {
8801              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8802 
8803              if (r == (Quantum *) NULL)
8804                break;
8805 
8806              for (x=0; x < (ssize_t) image->columns; x++)
8807              {
8808                 LBR04PixelRGBA(r);
8809                 r+=GetPixelChannels(image);
8810              }
8811 
8812              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8813                 break;
8814            }
8815 
8816            if (image->storage_class == PseudoClass && image->colormap != NULL)
8817            {
8818              for (i=0; i < (ssize_t) image->colors; i++)
8819              {
8820                LBR04PacketRGBA(image->colormap[i]);
8821              }
8822            }
8823          }
8824        else if (image->depth > 1)
8825          {
8826            /* Scale to 2-bit */
8827            LBR02PacketRGBA(image->background_color);
8828 
8829            for (y=0; y < (ssize_t) image->rows; y++)
8830            {
8831              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8832 
8833              if (r == (Quantum *) NULL)
8834                break;
8835 
8836              for (x=0; x < (ssize_t) image->columns; x++)
8837              {
8838                 LBR02PixelRGBA(r);
8839                 r+=GetPixelChannels(image);
8840              }
8841 
8842              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8843                 break;
8844            }
8845 
8846            if (image->storage_class == PseudoClass && image->colormap != NULL)
8847            {
8848              for (i=0; i < (ssize_t) image->colors; i++)
8849              {
8850                LBR02PacketRGBA(image->colormap[i]);
8851              }
8852            }
8853          }
8854        else
8855          {
8856            /* Scale to 1-bit */
8857            LBR01PacketRGBA(image->background_color);
8858 
8859            for (y=0; y < (ssize_t) image->rows; y++)
8860            {
8861              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8862 
8863              if (r == (Quantum *) NULL)
8864                break;
8865 
8866              for (x=0; x < (ssize_t) image->columns; x++)
8867              {
8868                 LBR01PixelRGBA(r);
8869                 r+=GetPixelChannels(image);
8870              }
8871 
8872              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8873                 break;
8874            }
8875 
8876            if (image->storage_class == PseudoClass && image->colormap != NULL)
8877            {
8878              for (i=0; i < (ssize_t) image->colors; i++)
8879              {
8880                LBR01PacketRGBA(image->colormap[i]);
8881              }
8882            }
8883          }
8884     }
8885 
8886   /* To do: set to next higher multiple of 8 */
8887   if (image->depth < 8)
8888      image->depth=8;
8889 
8890 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8891   /* PNG does not handle depths greater than 16 so reduce it even
8892    * if lossy
8893    */
8894   if (image->depth > 8)
8895       image->depth=16;
8896 #endif
8897 
8898 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8899   if (image->depth > 8)
8900     {
8901       /* To do: fill low byte properly */
8902       image->depth=16;
8903     }
8904 
8905   if (image->depth == 16 && mng_info->write_png_depth != 16)
8906     if (mng_info->write_png8 ||
8907         LosslessReduceDepthOK(image,exception) != MagickFalse)
8908       image->depth = 8;
8909 #endif
8910 
8911   image_colors = (int) image->colors;
8912   number_opaque = (int) image->colors;
8913   number_transparent = 0;
8914   number_semitransparent = 0;
8915 
8916   if (mng_info->write_png_colortype &&
8917      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8918      mng_info->write_png_colortype < 4 &&
8919      image->alpha_trait == UndefinedPixelTrait)))
8920   {
8921      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8922       * are not going to need the result.
8923       */
8924      if (mng_info->write_png_colortype == 1 ||
8925         mng_info->write_png_colortype == 5)
8926        ping_have_color=MagickFalse;
8927 
8928      if (image->alpha_trait != UndefinedPixelTrait)
8929        {
8930          number_transparent = 2;
8931          number_semitransparent = 1;
8932        }
8933   }
8934 
8935   if (mng_info->write_png_colortype < 7)
8936   {
8937   /* BUILD_PALETTE
8938    *
8939    * Normally we run this just once, but in the case of writing PNG8
8940    * we reduce the transparency to binary and run again, then if there
8941    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8942    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8943    * palette.  Then (To do) we take care of a final reduction that is only
8944    * needed if there are still 256 colors present and one of them has both
8945    * transparent and opaque instances.
8946    */
8947 
8948   tried_332 = MagickFalse;
8949   tried_333 = MagickFalse;
8950   tried_444 = MagickFalse;
8951 
8952   if (image->depth != GetImageDepth(image,exception))
8953     (void) SetImageDepth(image,image->depth,exception);
8954   for (j=0; j<6; j++)
8955   {
8956     /*
8957      * Sometimes we get DirectClass images that have 256 colors or fewer.
8958      * This code will build a colormap.
8959      *
8960      * Also, sometimes we get PseudoClass images with an out-of-date
8961      * colormap.  This code will replace the colormap with a new one.
8962      * Sometimes we get PseudoClass images that have more than 256 colors.
8963      * This code will delete the colormap and change the image to
8964      * DirectClass.
8965      *
8966      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8967      * even though it sometimes contains left-over non-opaque values.
8968      *
8969      * Also we gather some information (number of opaque, transparent,
8970      * and semitransparent pixels, and whether the image has any non-gray
8971      * pixels or only black-and-white pixels) that we might need later.
8972      *
8973      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8974      * we need to check for bogus non-opaque values, at least.
8975      */
8976 
8977    int
8978      n;
8979 
8980    PixelInfo
8981      opaque[260],
8982      semitransparent[260],
8983      transparent[260];
8984 
8985    register const Quantum
8986      *r;
8987 
8988    register Quantum
8989      *q;
8990 
8991    if (logging != MagickFalse)
8992      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8993          "    Enter BUILD_PALETTE:");
8994 
8995    if (logging != MagickFalse)
8996      {
8997        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8998              "      image->columns=%.20g",(double) image->columns);
8999        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9000              "      image->rows=%.20g",(double) image->rows);
9001        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9002              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
9003        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9004              "      image->depth=%.20g",(double) image->depth);
9005 
9006        if (image->storage_class == PseudoClass && image->colormap != NULL)
9007        {
9008          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9009              "      Original colormap:");
9010          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9011              "        i    (red,green,blue,alpha)");
9012 
9013          for (i=0; i < 256; i++)
9014          {
9015                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016                    "        %d    (%d,%d,%d,%d)",
9017                     (int) i,
9018                     (int) image->colormap[i].red,
9019                     (int) image->colormap[i].green,
9020                     (int) image->colormap[i].blue,
9021                     (int) image->colormap[i].alpha);
9022          }
9023 
9024          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
9025          {
9026            if (i > 255)
9027              {
9028                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9029                    "        %d    (%d,%d,%d,%d)",
9030                     (int) i,
9031                     (int) image->colormap[i].red,
9032                     (int) image->colormap[i].green,
9033                     (int) image->colormap[i].blue,
9034                     (int) image->colormap[i].alpha);
9035              }
9036          }
9037        }
9038 
9039        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9040            "      image->colors=%d",(int) image->colors);
9041 
9042        if (image->colors == 0)
9043          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044              "        (zero means unknown)");
9045 
9046        if (ping_preserve_colormap == MagickFalse)
9047          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9048               "      Regenerate the colormap");
9049      }
9050 
9051      image_colors=0;
9052      number_opaque = 0;
9053      number_semitransparent = 0;
9054      number_transparent = 0;
9055 
9056      for (y=0; y < (ssize_t) image->rows; y++)
9057      {
9058        r=GetVirtualPixels(image,0,y,image->columns,1,exception);
9059 
9060        if (r == (const Quantum *) NULL)
9061          break;
9062 
9063        for (x=0; x < (ssize_t) image->columns; x++)
9064        {
9065            if (image->alpha_trait == UndefinedPixelTrait ||
9066               GetPixelAlpha(image,r) == OpaqueAlpha)
9067              {
9068                if (number_opaque < 259)
9069                  {
9070                    if (number_opaque == 0)
9071                      {
9072                        GetPixelInfoPixel(image,r,opaque);
9073                        opaque[0].alpha=OpaqueAlpha;
9074                        number_opaque=1;
9075                      }
9076 
9077                    for (i=0; i< (ssize_t) number_opaque; i++)
9078                      {
9079                        if (IsColorEqual(image,r,opaque+i))
9080                          break;
9081                      }
9082 
9083                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
9084                      {
9085                        number_opaque++;
9086                        GetPixelInfoPixel(image,r,opaque+i);
9087                        opaque[i].alpha=OpaqueAlpha;
9088                      }
9089                  }
9090              }
9091            else if (GetPixelAlpha(image,r) == TransparentAlpha)
9092              {
9093                if (number_transparent < 259)
9094                  {
9095                    if (number_transparent == 0)
9096                      {
9097                        GetPixelInfoPixel(image,r,transparent);
9098                        ping_trans_color.red=(unsigned short)
9099                          GetPixelRed(image,r);
9100                        ping_trans_color.green=(unsigned short)
9101                          GetPixelGreen(image,r);
9102                        ping_trans_color.blue=(unsigned short)
9103                          GetPixelBlue(image,r);
9104                        ping_trans_color.gray=(unsigned short)
9105                          GetPixelGray(image,r);
9106                        number_transparent = 1;
9107                      }
9108 
9109                    for (i=0; i< (ssize_t) number_transparent; i++)
9110                      {
9111                        if (IsColorEqual(image,r,transparent+i))
9112                          break;
9113                      }
9114 
9115                    if (i ==  (ssize_t) number_transparent &&
9116                        number_transparent < 259)
9117                      {
9118                        number_transparent++;
9119                        GetPixelInfoPixel(image,r,transparent+i);
9120                      }
9121                  }
9122              }
9123            else
9124              {
9125                if (number_semitransparent < 259)
9126                  {
9127                    if (number_semitransparent == 0)
9128                      {
9129                        GetPixelInfoPixel(image,r,semitransparent);
9130                        number_semitransparent = 1;
9131                      }
9132 
9133                    for (i=0; i< (ssize_t) number_semitransparent; i++)
9134                      {
9135                        if (IsColorEqual(image,r,semitransparent+i)
9136                            && GetPixelAlpha(image,r) ==
9137                            semitransparent[i].alpha)
9138                          break;
9139                      }
9140 
9141                    if (i ==  (ssize_t) number_semitransparent &&
9142                        number_semitransparent < 259)
9143                      {
9144                        number_semitransparent++;
9145                        GetPixelInfoPixel(image,r,semitransparent+i);
9146                      }
9147                  }
9148              }
9149            r+=GetPixelChannels(image);
9150         }
9151      }
9152 
9153      if (mng_info->write_png8 == MagickFalse &&
9154          ping_exclude_bKGD == MagickFalse)
9155        {
9156          /* Add the background color to the palette, if it
9157           * isn't already there.
9158           */
9159           if (logging != MagickFalse)
9160             {
9161               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9162                   "      Check colormap for background (%d,%d,%d)",
9163                   (int) image->background_color.red,
9164                   (int) image->background_color.green,
9165                   (int) image->background_color.blue);
9166             }
9167           for (i=0; i<number_opaque; i++)
9168           {
9169              if (opaque[i].red == image->background_color.red &&
9170                  opaque[i].green == image->background_color.green &&
9171                  opaque[i].blue == image->background_color.blue)
9172                break;
9173           }
9174           if (number_opaque < 259 && i == number_opaque)
9175             {
9176                opaque[i] = image->background_color;
9177                ping_background.index = i;
9178                number_opaque++;
9179                if (logging != MagickFalse)
9180                  {
9181                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9182                        "      background_color index is %d",(int) i);
9183                  }
9184 
9185             }
9186           else if (logging != MagickFalse)
9187               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9188                   "      No room in the colormap to add background color");
9189        }
9190 
9191      image_colors=number_opaque+number_transparent+number_semitransparent;
9192 
9193      if (logging != MagickFalse)
9194        {
9195          if (image_colors > 256)
9196             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9197                   "      image has more than 256 colors");
9198 
9199          else
9200             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9201                   "      image has %d colors",image_colors);
9202        }
9203 
9204      if (ping_preserve_colormap != MagickFalse)
9205        break;
9206 
9207      if (mng_info->write_png_colortype != 7) /* We won't need this info */
9208        {
9209          ping_have_color=MagickFalse;
9210          ping_have_non_bw=MagickFalse;
9211 
9212          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9213          {
9214            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9215               "incompatible colorspace");
9216            ping_have_color=MagickTrue;
9217            ping_have_non_bw=MagickTrue;
9218          }
9219 
9220          if(image_colors > 256)
9221            {
9222              for (y=0; y < (ssize_t) image->rows; y++)
9223              {
9224                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9225 
9226                if (q == (Quantum *) NULL)
9227                  break;
9228 
9229                r=q;
9230                for (x=0; x < (ssize_t) image->columns; x++)
9231                {
9232                  if (GetPixelRed(image,r) != GetPixelGreen(image,r) ||
9233                      GetPixelRed(image,r) != GetPixelBlue(image,r))
9234                    {
9235                       ping_have_color=MagickTrue;
9236                       ping_have_non_bw=MagickTrue;
9237                       break;
9238                    }
9239                  r+=GetPixelChannels(image);
9240                }
9241 
9242                if (ping_have_color != MagickFalse)
9243                  break;
9244 
9245                /* Worst case is black-and-white; we are looking at every
9246                 * pixel twice.
9247                 */
9248 
9249                if (ping_have_non_bw == MagickFalse)
9250                  {
9251                    r=q;
9252                    for (x=0; x < (ssize_t) image->columns; x++)
9253                    {
9254                      if (GetPixelRed(image,r) != 0 &&
9255                          GetPixelRed(image,r) != QuantumRange)
9256                        {
9257                          ping_have_non_bw=MagickTrue;
9258                          break;
9259                        }
9260                      r+=GetPixelChannels(image);
9261                    }
9262                }
9263              }
9264            }
9265        }
9266 
9267      if (image_colors < 257)
9268        {
9269          PixelInfo
9270            colormap[260];
9271 
9272          /*
9273           * Initialize image colormap.
9274           */
9275 
9276          if (logging != MagickFalse)
9277             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9278                   "      Sort the new colormap");
9279 
9280         /* Sort palette, transparent first */;
9281 
9282          n = 0;
9283 
9284          for (i=0; i<number_transparent; i++)
9285             colormap[n++] = transparent[i];
9286 
9287          for (i=0; i<number_semitransparent; i++)
9288             colormap[n++] = semitransparent[i];
9289 
9290          for (i=0; i<number_opaque; i++)
9291             colormap[n++] = opaque[i];
9292 
9293          ping_background.index +=
9294            (number_transparent + number_semitransparent);
9295 
9296          /* image_colors < 257; search the colormap instead of the pixels
9297           * to get ping_have_color and ping_have_non_bw
9298           */
9299          for (i=0; i<n; i++)
9300          {
9301            if (ping_have_color == MagickFalse)
9302              {
9303                 if (colormap[i].red != colormap[i].green ||
9304                     colormap[i].red != colormap[i].blue)
9305                   {
9306                      ping_have_color=MagickTrue;
9307                      ping_have_non_bw=MagickTrue;
9308                      break;
9309                   }
9310               }
9311 
9312            if (ping_have_non_bw == MagickFalse)
9313              {
9314                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9315                    ping_have_non_bw=MagickTrue;
9316              }
9317           }
9318 
9319         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9320             (number_transparent == 0 && number_semitransparent == 0)) &&
9321             (((mng_info->write_png_colortype-1) ==
9322             PNG_COLOR_TYPE_PALETTE) ||
9323             (mng_info->write_png_colortype == 0)))
9324           {
9325             if (logging != MagickFalse)
9326               {
9327                 if (n !=  (ssize_t) image_colors)
9328                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9329                    "   image_colors (%d) and n (%d)  don't match",
9330                    image_colors, n);
9331 
9332                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9333                    "      AcquireImageColormap");
9334               }
9335 
9336             image->colors = image_colors;
9337 
9338             if (AcquireImageColormap(image,image_colors,exception) == MagickFalse)
9339               {
9340                 (void) ThrowMagickException(exception,GetMagickModule(),
9341                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
9342                   image->filename);
9343                 break;
9344               }
9345 
9346             for (i=0; i< (ssize_t) image_colors; i++)
9347                image->colormap[i] = colormap[i];
9348 
9349             if (logging != MagickFalse)
9350               {
9351                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9352                       "      image->colors=%d (%d)",
9353                       (int) image->colors, image_colors);
9354 
9355                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9356                       "      Update the pixel indexes");
9357               }
9358 
9359             /* Sync the pixel indices with the new colormap */
9360 
9361             for (y=0; y < (ssize_t) image->rows; y++)
9362             {
9363               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9364 
9365               if (q == (Quantum *) NULL)
9366                 break;
9367 
9368               for (x=0; x < (ssize_t) image->columns; x++)
9369               {
9370                 for (i=0; i< (ssize_t) image_colors; i++)
9371                 {
9372                   if ((image->alpha_trait == UndefinedPixelTrait ||
9373                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9374                       image->colormap[i].red == GetPixelRed(image,q) &&
9375                       image->colormap[i].green == GetPixelGreen(image,q) &&
9376                       image->colormap[i].blue == GetPixelBlue(image,q))
9377                   {
9378                     SetPixelIndex(image,i,q);
9379                     break;
9380                   }
9381                 }
9382                 q+=GetPixelChannels(image);
9383               }
9384 
9385               if (SyncAuthenticPixels(image,exception) == MagickFalse)
9386                  break;
9387             }
9388           }
9389        }
9390 
9391      if (logging != MagickFalse)
9392        {
9393          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9394             "      image->colors=%d", (int) image->colors);
9395 
9396          if (image->colormap != NULL)
9397            {
9398              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9399                  "       i     (red,green,blue,alpha)");
9400 
9401              for (i=0; i < (ssize_t) image->colors; i++)
9402              {
9403                if (i < 300 || i >= (ssize_t) image->colors - 10)
9404                  {
9405                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9406                        "       %d     (%d,%d,%d,%d)",
9407                         (int) i,
9408                         (int) image->colormap[i].red,
9409                         (int) image->colormap[i].green,
9410                         (int) image->colormap[i].blue,
9411                         (int) image->colormap[i].alpha);
9412                  }
9413              }
9414            }
9415 
9416            if (number_transparent < 257)
9417              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9418                    "      number_transparent     = %d",
9419                    number_transparent);
9420            else
9421 
9422              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9423                    "      number_transparent     > 256");
9424 
9425            if (number_opaque < 257)
9426              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9427                    "      number_opaque          = %d",
9428                    number_opaque);
9429 
9430            else
9431              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9432                    "      number_opaque          > 256");
9433 
9434            if (number_semitransparent < 257)
9435              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9436                    "      number_semitransparent = %d",
9437                    number_semitransparent);
9438 
9439            else
9440              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9441                    "      number_semitransparent > 256");
9442 
9443            if (ping_have_non_bw == MagickFalse)
9444               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9445                     "      All pixels and the background are black or white");
9446 
9447            else if (ping_have_color == MagickFalse)
9448               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449                     "      All pixels and the background are gray");
9450 
9451            else
9452               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9453                     "      At least one pixel or the background is non-gray");
9454 
9455            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9456                "    Exit BUILD_PALETTE:");
9457        }
9458 
9459    if (mng_info->write_png8 == MagickFalse)
9460       break;
9461 
9462    /* Make any reductions necessary for the PNG8 format */
9463     if (image_colors <= 256 &&
9464         image_colors != 0 && image->colormap != NULL &&
9465         number_semitransparent == 0 &&
9466         number_transparent <= 1)
9467       break;
9468 
9469     /* PNG8 can't have semitransparent colors so we threshold the
9470      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9471      * transparent color so if more than one is transparent we merge
9472      * them into image->background_color.
9473      */
9474     if (number_semitransparent != 0 || number_transparent > 1)
9475       {
9476         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9477             "    Thresholding the alpha channel to binary");
9478 
9479         for (y=0; y < (ssize_t) image->rows; y++)
9480         {
9481           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9482 
9483           if (q == (Quantum *) NULL)
9484             break;
9485 
9486           for (x=0; x < (ssize_t) image->columns; x++)
9487           {
9488               if (GetPixelAlpha(image,q) < OpaqueAlpha/2)
9489                 {
9490                   SetPixelViaPixelInfo(image,&image->background_color,q);
9491                   SetPixelAlpha(image,TransparentAlpha,q);
9492                 }
9493               else
9494                   SetPixelAlpha(image,OpaqueAlpha,q);
9495               q+=GetPixelChannels(image);
9496           }
9497 
9498           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9499              break;
9500 
9501           if (image_colors != 0 && image_colors <= 256 &&
9502              image->colormap != NULL)
9503             for (i=0; i<image_colors; i++)
9504                 image->colormap[i].alpha =
9505                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9506                     TransparentAlpha : OpaqueAlpha);
9507         }
9508       continue;
9509     }
9510 
9511     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9512      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9513      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9514      * colors or less.
9515      */
9516     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9517       {
9518         if (logging != MagickFalse)
9519            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9520                "    Quantizing the background color to 4-4-4");
9521 
9522         tried_444 = MagickTrue;
9523 
9524         LBR04PacketRGB(image->background_color);
9525 
9526         if (logging != MagickFalse)
9527           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9528               "    Quantizing the pixel colors to 4-4-4");
9529 
9530         if (image->colormap == NULL)
9531         {
9532           for (y=0; y < (ssize_t) image->rows; y++)
9533           {
9534             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9535 
9536             if (q == (Quantum *) NULL)
9537               break;
9538 
9539             for (x=0; x < (ssize_t) image->columns; x++)
9540             {
9541               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9542                   LBR04PixelRGB(q);
9543               q+=GetPixelChannels(image);
9544             }
9545 
9546             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9547                break;
9548           }
9549         }
9550 
9551         else /* Should not reach this; colormap already exists and
9552                 must be <= 256 */
9553         {
9554           if (logging != MagickFalse)
9555               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9556               "    Quantizing the colormap to 4-4-4");
9557 
9558           for (i=0; i<image_colors; i++)
9559           {
9560             LBR04PacketRGB(image->colormap[i]);
9561           }
9562         }
9563         continue;
9564       }
9565 
9566     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9567       {
9568         if (logging != MagickFalse)
9569            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9570                "    Quantizing the background color to 3-3-3");
9571 
9572         tried_333 = MagickTrue;
9573 
9574         LBR03PacketRGB(image->background_color);
9575 
9576         if (logging != MagickFalse)
9577           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9578               "    Quantizing the pixel colors to 3-3-3-1");
9579 
9580         if (image->colormap == NULL)
9581         {
9582           for (y=0; y < (ssize_t) image->rows; y++)
9583           {
9584             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9585 
9586             if (q == (Quantum *) NULL)
9587               break;
9588 
9589             for (x=0; x < (ssize_t) image->columns; x++)
9590             {
9591               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9592                   LBR03RGB(q);
9593               q+=GetPixelChannels(image);
9594             }
9595 
9596             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9597                break;
9598           }
9599         }
9600 
9601         else /* Should not reach this; colormap already exists and
9602                 must be <= 256 */
9603         {
9604           if (logging != MagickFalse)
9605               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9606               "    Quantizing the colormap to 3-3-3-1");
9607           for (i=0; i<image_colors; i++)
9608           {
9609               LBR03PacketRGB(image->colormap[i]);
9610           }
9611         }
9612         continue;
9613       }
9614 
9615     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9616       {
9617         if (logging != MagickFalse)
9618            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9619                "    Quantizing the background color to 3-3-2");
9620 
9621         tried_332 = MagickTrue;
9622 
9623         /* Red and green were already done so we only quantize the blue
9624          * channel
9625          */
9626 
9627         LBR02PacketBlue(image->background_color);
9628 
9629         if (logging != MagickFalse)
9630           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631               "    Quantizing the pixel colors to 3-3-2-1");
9632 
9633         if (image->colormap == NULL)
9634         {
9635           for (y=0; y < (ssize_t) image->rows; y++)
9636           {
9637             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9638 
9639             if (q == (Quantum *) NULL)
9640               break;
9641 
9642             for (x=0; x < (ssize_t) image->columns; x++)
9643             {
9644               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9645                   LBR02PixelBlue(q);
9646               q+=GetPixelChannels(image);
9647             }
9648 
9649             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9650                break;
9651           }
9652         }
9653 
9654         else /* Should not reach this; colormap already exists and
9655                 must be <= 256 */
9656         {
9657           if (logging != MagickFalse)
9658               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9659               "    Quantizing the colormap to 3-3-2-1");
9660           for (i=0; i<image_colors; i++)
9661           {
9662               LBR02PacketBlue(image->colormap[i]);
9663           }
9664       }
9665       continue;
9666     }
9667 
9668     if (image_colors == 0 || image_colors > 256)
9669     {
9670       /* Take care of special case with 256 opaque colors + 1 transparent
9671        * color.  We don't need to quantize to 2-3-2-1; we only need to
9672        * eliminate one color, so we'll merge the two darkest red
9673        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9674        */
9675       if (logging != MagickFalse)
9676         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9677             "    Merging two dark red background colors to 3-3-2-1");
9678 
9679       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9680           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9681           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9682       {
9683          image->background_color.red=ScaleCharToQuantum(0x24);
9684       }
9685 
9686       if (logging != MagickFalse)
9687         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9688             "    Merging two dark red pixel colors to 3-3-2-1");
9689 
9690       if (image->colormap == NULL)
9691       {
9692         for (y=0; y < (ssize_t) image->rows; y++)
9693         {
9694           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9695 
9696           if (q == (Quantum *) NULL)
9697             break;
9698 
9699           for (x=0; x < (ssize_t) image->columns; x++)
9700           {
9701             if (ScaleQuantumToChar(GetPixelRed(image,q)) == 0x49 &&
9702                 ScaleQuantumToChar(GetPixelGreen(image,q)) == 0x00 &&
9703                 ScaleQuantumToChar(GetPixelBlue(image,q)) == 0x00 &&
9704                 GetPixelAlpha(image,q) == OpaqueAlpha)
9705               {
9706                 SetPixelRed(image,ScaleCharToQuantum(0x24),q);
9707               }
9708             q+=GetPixelChannels(image);
9709           }
9710 
9711           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9712              break;
9713 
9714         }
9715       }
9716 
9717       else
9718       {
9719          for (i=0; i<image_colors; i++)
9720          {
9721             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9722                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9723                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9724             {
9725                image->colormap[i].red=ScaleCharToQuantum(0x24);
9726             }
9727          }
9728       }
9729     }
9730   }
9731   }
9732   /* END OF BUILD_PALETTE */
9733 
9734   /* If we are excluding the tRNS chunk and there is transparency,
9735    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9736    * PNG.
9737    */
9738   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9739      (number_transparent != 0 || number_semitransparent != 0))
9740     {
9741       unsigned int colortype=mng_info->write_png_colortype;
9742 
9743       if (ping_have_color == MagickFalse)
9744         mng_info->write_png_colortype = 5;
9745 
9746       else
9747         mng_info->write_png_colortype = 7;
9748 
9749       if (colortype != 0 &&
9750          mng_info->write_png_colortype != colortype)
9751         ping_need_colortype_warning=MagickTrue;
9752 
9753     }
9754 
9755   /* See if cheap transparency is possible.  It is only possible
9756    * when there is a single transparent color, no semitransparent
9757    * color, and no opaque color that has the same RGB components
9758    * as the transparent color.  We only need this information if
9759    * we are writing a PNG with colortype 0 or 2, and we have not
9760    * excluded the tRNS chunk.
9761    */
9762   if (number_transparent == 1 &&
9763       mng_info->write_png_colortype < 4)
9764     {
9765        ping_have_cheap_transparency = MagickTrue;
9766 
9767        if (number_semitransparent != 0)
9768          ping_have_cheap_transparency = MagickFalse;
9769 
9770        else if (image_colors == 0 || image_colors > 256 ||
9771            image->colormap == NULL)
9772          {
9773            register const Quantum
9774              *q;
9775 
9776            for (y=0; y < (ssize_t) image->rows; y++)
9777            {
9778              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9779 
9780              if (q == (Quantum *) NULL)
9781                break;
9782 
9783              for (x=0; x < (ssize_t) image->columns; x++)
9784              {
9785                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9786                      (unsigned short) GetPixelRed(image,q) ==
9787                                      ping_trans_color.red &&
9788                      (unsigned short) GetPixelGreen(image,q) ==
9789                                      ping_trans_color.green &&
9790                      (unsigned short) GetPixelBlue(image,q) ==
9791                                      ping_trans_color.blue)
9792                    {
9793                      ping_have_cheap_transparency = MagickFalse;
9794                      break;
9795                    }
9796 
9797                  q+=GetPixelChannels(image);
9798              }
9799 
9800              if (ping_have_cheap_transparency == MagickFalse)
9801                 break;
9802            }
9803          }
9804        else
9805          {
9806             /* Assuming that image->colormap[0] is the one transparent color
9807              * and that all others are opaque.
9808              */
9809             if (image_colors > 1)
9810               for (i=1; i<image_colors; i++)
9811                 if (image->colormap[i].red == image->colormap[0].red &&
9812                     image->colormap[i].green == image->colormap[0].green &&
9813                     image->colormap[i].blue == image->colormap[0].blue)
9814                   {
9815                      ping_have_cheap_transparency = MagickFalse;
9816                      break;
9817                   }
9818          }
9819 
9820        if (logging != MagickFalse)
9821          {
9822            if (ping_have_cheap_transparency == MagickFalse)
9823              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9824                  "   Cheap transparency is not possible.");
9825 
9826            else
9827              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828                  "   Cheap transparency is possible.");
9829          }
9830      }
9831   else
9832     ping_have_cheap_transparency = MagickFalse;
9833 
9834   image_depth=image->depth;
9835 
9836   quantum_info = (QuantumInfo *) NULL;
9837   number_colors=0;
9838   image_colors=(int) image->colors;
9839   image_matte=image->alpha_trait !=
9840         UndefinedPixelTrait ? MagickTrue : MagickFalse;
9841 
9842   if (mng_info->write_png_colortype < 5)
9843     mng_info->IsPalette=image->storage_class == PseudoClass &&
9844       image_colors <= 256 && image->colormap != NULL;
9845   else
9846     mng_info->IsPalette = MagickFalse;
9847 
9848   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9849      (image->colors == 0 || image->colormap == NULL))
9850     {
9851       image_info=DestroyImageInfo(image_info);
9852       image=DestroyImage(image);
9853       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9854           "Cannot write PNG8 or color-type 3; colormap is NULL",
9855           "`%s'",IMimage->filename);
9856       return(MagickFalse);
9857     }
9858 
9859   /*
9860     Allocate the PNG structures
9861   */
9862 #ifdef PNG_USER_MEM_SUPPORTED
9863  error_info.image=image;
9864  error_info.exception=exception;
9865   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9866     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9867     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9868 
9869 #else
9870   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9871     MagickPNGErrorHandler,MagickPNGWarningHandler);
9872 
9873 #endif
9874   if (ping == (png_struct *) NULL)
9875     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9876 
9877   ping_info=png_create_info_struct(ping);
9878 
9879   if (ping_info == (png_info *) NULL)
9880     {
9881       png_destroy_write_struct(&ping,(png_info **) NULL);
9882       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9883     }
9884 
9885   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9886   pixel_info=(MemoryInfo *) NULL;
9887 
9888   if (setjmp(png_jmpbuf(ping)))
9889     {
9890       /*
9891         PNG write failed.
9892       */
9893 #ifdef PNG_DEBUG
9894      if (image_info->verbose)
9895         (void) printf("PNG write has failed.\n");
9896 #endif
9897       png_destroy_write_struct(&ping,&ping_info);
9898 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9899       UnlockSemaphoreInfo(ping_semaphore);
9900 #endif
9901 
9902       if (pixel_info != (MemoryInfo *) NULL)
9903         pixel_info=RelinquishVirtualMemory(pixel_info);
9904 
9905       if (quantum_info != (QuantumInfo *) NULL)
9906         quantum_info=DestroyQuantumInfo(quantum_info);
9907 
9908       if (ping_have_blob != MagickFalse)
9909           (void) CloseBlob(image);
9910       image_info=DestroyImageInfo(image_info);
9911       image=DestroyImage(image);
9912       return(MagickFalse);
9913     }
9914 
9915   /* {  For navigation to end of SETJMP-protected block.  Within this
9916    *    block, use png_error() instead of Throwing an Exception, to ensure
9917    *    that libpng is able to clean up, and that the semaphore is unlocked.
9918    */
9919 
9920 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9921   LockSemaphoreInfo(ping_semaphore);
9922 #endif
9923 
9924 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9925   /* Allow benign errors */
9926   png_set_benign_errors(ping, 1);
9927 #endif
9928 
9929 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9930   /* Reject images with too many rows or columns */
9931   png_set_user_limits(ping,
9932     (png_uint_32) MagickMin(0x7fffffffL,
9933         GetMagickResourceLimit(WidthResource)),
9934     (png_uint_32) MagickMin(0x7fffffffL,
9935         GetMagickResourceLimit(HeightResource)));
9936 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9937 
9938   /*
9939     Prepare PNG for writing.
9940   */
9941 
9942 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9943   if (mng_info->write_mng)
9944   {
9945      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9946 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9947      /* Disable new libpng-1.5.10 feature when writing a MNG because
9948       * zero-length PLTE is OK
9949       */
9950      png_set_check_for_invalid_index (ping, 0);
9951 # endif
9952   }
9953 
9954 #else
9955 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9956   if (mng_info->write_mng)
9957      png_permit_empty_plte(ping,MagickTrue);
9958 
9959 # endif
9960 #endif
9961 
9962   x=0;
9963 
9964   ping_width=(png_uint_32) image->columns;
9965   ping_height=(png_uint_32) image->rows;
9966 
9967   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9968      image_depth=8;
9969 
9970   if (mng_info->write_png48 || mng_info->write_png64)
9971      image_depth=16;
9972 
9973   if (mng_info->write_png_depth != 0)
9974      image_depth=mng_info->write_png_depth;
9975 
9976   /* Adjust requested depth to next higher valid depth if necessary */
9977   if (image_depth > 8)
9978      image_depth=16;
9979 
9980   if ((image_depth > 4) && (image_depth < 8))
9981      image_depth=8;
9982 
9983   if (image_depth == 3)
9984      image_depth=4;
9985 
9986   if (logging != MagickFalse)
9987     {
9988      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9989         "    width=%.20g",(double) ping_width);
9990      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9991         "    height=%.20g",(double) ping_height);
9992      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9993         "    image_matte=%.20g",(double) image->alpha_trait);
9994      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9995         "    image->depth=%.20g",(double) image->depth);
9996      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9997         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9998     }
9999 
10000   save_image_depth=image_depth;
10001   ping_bit_depth=(png_byte) save_image_depth;
10002 
10003 
10004 #if defined(PNG_pHYs_SUPPORTED)
10005   if (ping_exclude_pHYs == MagickFalse)
10006   {
10007   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
10008       (!mng_info->write_mng || !mng_info->equal_physs))
10009     {
10010       if (logging != MagickFalse)
10011         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10012             "    Setting up pHYs chunk");
10013 
10014       if (image->units == PixelsPerInchResolution)
10015         {
10016           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
10017           ping_pHYs_x_resolution=
10018              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
10019           ping_pHYs_y_resolution=
10020              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
10021         }
10022 
10023       else if (image->units == PixelsPerCentimeterResolution)
10024         {
10025           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
10026           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
10027           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
10028         }
10029 
10030       else
10031         {
10032           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
10033           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
10034           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
10035         }
10036 
10037       if (logging != MagickFalse)
10038         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10039           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
10040           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
10041           (int) ping_pHYs_unit_type);
10042        ping_have_pHYs = MagickTrue;
10043     }
10044   }
10045 #endif
10046 
10047   if (ping_exclude_bKGD == MagickFalse)
10048   {
10049   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
10050     {
10051        unsigned int
10052          mask;
10053 
10054        mask=0xffff;
10055        if (ping_bit_depth == 8)
10056           mask=0x00ff;
10057 
10058        if (ping_bit_depth == 4)
10059           mask=0x000f;
10060 
10061        if (ping_bit_depth == 2)
10062           mask=0x0003;
10063 
10064        if (ping_bit_depth == 1)
10065           mask=0x0001;
10066 
10067        ping_background.red=(png_uint_16)
10068          (ScaleQuantumToShort(image->background_color.red) & mask);
10069 
10070        ping_background.green=(png_uint_16)
10071          (ScaleQuantumToShort(image->background_color.green) & mask);
10072 
10073        ping_background.blue=(png_uint_16)
10074          (ScaleQuantumToShort(image->background_color.blue) & mask);
10075 
10076        ping_background.gray=(png_uint_16) ping_background.green;
10077     }
10078 
10079   if (logging != MagickFalse)
10080     {
10081       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10082           "    Setting up bKGD chunk (1)");
10083       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10084           "      background_color index is %d",
10085           (int) ping_background.index);
10086 
10087       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10088           "    ping_bit_depth=%d",ping_bit_depth);
10089     }
10090 
10091   ping_have_bKGD = MagickTrue;
10092   }
10093 
10094   /*
10095     Select the color type.
10096   */
10097   matte=image_matte;
10098   old_bit_depth=0;
10099 
10100   if (mng_info->IsPalette && mng_info->write_png8)
10101     {
10102       /* To do: make this a function cause it's used twice, except
10103          for reducing the sample depth from 8. */
10104 
10105       number_colors=image_colors;
10106 
10107       ping_have_tRNS=MagickFalse;
10108 
10109       /*
10110         Set image palette.
10111       */
10112       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10113 
10114       if (logging != MagickFalse)
10115         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10116             "  Setting up PLTE chunk with %d colors (%d)",
10117             number_colors, image_colors);
10118 
10119       for (i=0; i < (ssize_t) number_colors; i++)
10120       {
10121         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10122         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10123         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10124         if (logging != MagickFalse)
10125           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10126 #if MAGICKCORE_QUANTUM_DEPTH == 8
10127             "    %3ld (%3d,%3d,%3d)",
10128 #else
10129             "    %5ld (%5d,%5d,%5d)",
10130 #endif
10131             (long) i,palette[i].red,palette[i].green,palette[i].blue);
10132 
10133       }
10134 
10135       ping_have_PLTE=MagickTrue;
10136       image_depth=ping_bit_depth;
10137       ping_num_trans=0;
10138 
10139       if (matte != MagickFalse)
10140       {
10141           /*
10142             Identify which colormap entry is transparent.
10143           */
10144           assert(number_colors <= 256);
10145           assert(image->colormap != NULL);
10146 
10147           for (i=0; i < (ssize_t) number_transparent; i++)
10148              ping_trans_alpha[i]=0;
10149 
10150 
10151           ping_num_trans=(unsigned short) (number_transparent +
10152              number_semitransparent);
10153 
10154           if (ping_num_trans == 0)
10155              ping_have_tRNS=MagickFalse;
10156 
10157           else
10158              ping_have_tRNS=MagickTrue;
10159       }
10160 
10161       if (ping_exclude_bKGD == MagickFalse)
10162       {
10163        /*
10164         * Identify which colormap entry is the background color.
10165         */
10166 
10167         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10168           if (IsPNGColorEqual(ping_background,image->colormap[i]))
10169             break;
10170 
10171         ping_background.index=(png_byte) i;
10172 
10173         if (logging != MagickFalse)
10174           {
10175             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10176                  "      background_color index is %d",
10177                  (int) ping_background.index);
10178           }
10179       }
10180     } /* end of write_png8 */
10181 
10182   else if (mng_info->write_png_colortype == 1)
10183     {
10184       image_matte=MagickFalse;
10185       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10186     }
10187 
10188   else if (mng_info->write_png24 || mng_info->write_png48 ||
10189       mng_info->write_png_colortype == 3)
10190     {
10191       image_matte=MagickFalse;
10192       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10193     }
10194 
10195   else if (mng_info->write_png32 || mng_info->write_png64 ||
10196       mng_info->write_png_colortype == 7)
10197     {
10198       image_matte=MagickTrue;
10199       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10200     }
10201 
10202   else /* mng_info->write_pngNN not specified */
10203     {
10204       image_depth=ping_bit_depth;
10205 
10206       if (mng_info->write_png_colortype != 0)
10207         {
10208           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10209 
10210           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10211               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10212             image_matte=MagickTrue;
10213 
10214           else
10215             image_matte=MagickFalse;
10216 
10217           if (logging != MagickFalse)
10218              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10219              "   PNG colortype %d was specified:",(int) ping_color_type);
10220         }
10221 
10222       else /* write_png_colortype not specified */
10223         {
10224           if (logging != MagickFalse)
10225              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226              "  Selecting PNG colortype:");
10227 
10228           if (image_info->type == TrueColorType)
10229             {
10230               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10231               image_matte=MagickFalse;
10232             }
10233           else if (image_info->type == TrueColorAlphaType)
10234             {
10235               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10236               image_matte=MagickTrue;
10237             }
10238           else if (image_info->type == PaletteType ||
10239                    image_info->type == PaletteAlphaType)
10240             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10241           else
10242             {
10243               if (ping_have_color == MagickFalse)
10244                 {
10245                   if (image_matte == MagickFalse)
10246                     {
10247                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10248                       image_matte=MagickFalse;
10249                     }
10250 
10251                   else
10252                     {
10253                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10254                       image_matte=MagickTrue;
10255                     }
10256                 }
10257               else
10258                 {
10259                   if (image_matte == MagickFalse)
10260                     {
10261                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10262                       image_matte=MagickFalse;
10263                     }
10264 
10265                   else
10266                     {
10267                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10268                       image_matte=MagickTrue;
10269                     }
10270                  }
10271             }
10272 
10273         }
10274 
10275       if (logging != MagickFalse)
10276          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10277          "    Selected PNG colortype=%d",ping_color_type);
10278 
10279       if (ping_bit_depth < 8)
10280         {
10281           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10282               ping_color_type == PNG_COLOR_TYPE_RGB ||
10283               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10284             ping_bit_depth=8;
10285         }
10286 
10287       old_bit_depth=ping_bit_depth;
10288 
10289       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10290         {
10291           if (image->alpha_trait == UndefinedPixelTrait &&
10292                ping_have_non_bw == MagickFalse)
10293              ping_bit_depth=1;
10294         }
10295 
10296       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10297         {
10298            size_t one = 1;
10299            ping_bit_depth=1;
10300 
10301            if (image->colors == 0)
10302            {
10303               /* DO SOMETHING */
10304                 png_error(ping,"image has 0 colors");
10305            }
10306 
10307            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10308              ping_bit_depth <<= 1;
10309         }
10310 
10311       if (logging != MagickFalse)
10312          {
10313            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10314             "    Number of colors: %.20g",(double) image_colors);
10315 
10316            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10317             "    Tentative PNG bit depth: %d",ping_bit_depth);
10318          }
10319 
10320       if (ping_bit_depth < (int) mng_info->write_png_depth)
10321          ping_bit_depth = mng_info->write_png_depth;
10322     }
10323   (void) old_bit_depth;
10324   image_depth=ping_bit_depth;
10325 
10326   if (logging != MagickFalse)
10327     {
10328       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10329         "    Tentative PNG color type: %s (%.20g)",
10330         PngColorTypeToString(ping_color_type),
10331         (double) ping_color_type);
10332 
10333       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10334         "    image_info->type: %.20g",(double) image_info->type);
10335 
10336       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10337         "    image_depth: %.20g",(double) image_depth);
10338 
10339       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10340 
10341         "    image->depth: %.20g",(double) image->depth);
10342 
10343       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10344         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
10345     }
10346 
10347   if (matte != MagickFalse)
10348     {
10349       if (mng_info->IsPalette)
10350         {
10351           if (mng_info->write_png_colortype == 0)
10352             {
10353               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10354 
10355               if (ping_have_color != MagickFalse)
10356                  ping_color_type=PNG_COLOR_TYPE_RGBA;
10357             }
10358 
10359           /*
10360            * Determine if there is any transparent color.
10361           */
10362           if (number_transparent + number_semitransparent == 0)
10363             {
10364               /*
10365                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
10366               */
10367 
10368               image_matte=MagickFalse;
10369 
10370               if (mng_info->write_png_colortype == 0)
10371                 ping_color_type&=0x03;
10372             }
10373 
10374           else
10375             {
10376               unsigned int
10377                 mask;
10378 
10379               mask=0xffff;
10380 
10381               if (ping_bit_depth == 8)
10382                  mask=0x00ff;
10383 
10384               if (ping_bit_depth == 4)
10385                  mask=0x000f;
10386 
10387               if (ping_bit_depth == 2)
10388                  mask=0x0003;
10389 
10390               if (ping_bit_depth == 1)
10391                  mask=0x0001;
10392 
10393               ping_trans_color.red=(png_uint_16)
10394                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10395 
10396               ping_trans_color.green=(png_uint_16)
10397                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10398 
10399               ping_trans_color.blue=(png_uint_16)
10400                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10401 
10402               ping_trans_color.gray=(png_uint_16)
10403                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10404                    image->colormap)) & mask);
10405 
10406               ping_trans_color.index=(png_byte) 0;
10407 
10408               ping_have_tRNS=MagickTrue;
10409             }
10410 
10411           if (ping_have_tRNS != MagickFalse)
10412             {
10413               /*
10414                * Determine if there is one and only one transparent color
10415                * and if so if it is fully transparent.
10416                */
10417               if (ping_have_cheap_transparency == MagickFalse)
10418                 ping_have_tRNS=MagickFalse;
10419             }
10420 
10421           if (ping_have_tRNS != MagickFalse)
10422             {
10423               if (mng_info->write_png_colortype == 0)
10424                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10425 
10426               if (image_depth == 8)
10427                 {
10428                   ping_trans_color.red&=0xff;
10429                   ping_trans_color.green&=0xff;
10430                   ping_trans_color.blue&=0xff;
10431                   ping_trans_color.gray&=0xff;
10432                 }
10433             }
10434         }
10435       else
10436         {
10437           if (image_depth == 8)
10438             {
10439               ping_trans_color.red&=0xff;
10440               ping_trans_color.green&=0xff;
10441               ping_trans_color.blue&=0xff;
10442               ping_trans_color.gray&=0xff;
10443             }
10444         }
10445     }
10446 
10447     matte=image_matte;
10448 
10449     if (ping_have_tRNS != MagickFalse)
10450       image_matte=MagickFalse;
10451 
10452     if ((mng_info->IsPalette) &&
10453         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10454         ping_have_color == MagickFalse &&
10455         (image_matte == MagickFalse || image_depth >= 8))
10456       {
10457         size_t one=1;
10458 
10459         if (image_matte != MagickFalse)
10460           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10461 
10462         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10463           {
10464             ping_color_type=PNG_COLOR_TYPE_GRAY;
10465 
10466             if (save_image_depth == 16 && image_depth == 8)
10467               {
10468                 if (logging != MagickFalse)
10469                   {
10470                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10471                         "  Scaling ping_trans_color (0)");
10472                   }
10473                     ping_trans_color.gray*=0x0101;
10474               }
10475           }
10476 
10477         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10478           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10479 
10480         if ((image_colors == 0) ||
10481              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10482           image_colors=(int) (one << image_depth);
10483 
10484         if (image_depth > 8)
10485           ping_bit_depth=16;
10486 
10487         else
10488           {
10489             ping_bit_depth=8;
10490             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10491               {
10492                 if(!mng_info->write_png_depth)
10493                   {
10494                     ping_bit_depth=1;
10495 
10496                     while ((int) (one << ping_bit_depth)
10497                         < (ssize_t) image_colors)
10498                       ping_bit_depth <<= 1;
10499                   }
10500               }
10501 
10502             else if (ping_color_type ==
10503                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10504                 mng_info->IsPalette)
10505               {
10506               /* Check if grayscale is reducible */
10507 
10508                 int
10509                   depth_4_ok=MagickTrue,
10510                   depth_2_ok=MagickTrue,
10511                   depth_1_ok=MagickTrue;
10512 
10513                 for (i=0; i < (ssize_t) image_colors; i++)
10514                 {
10515                    unsigned char
10516                      intensity;
10517 
10518                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10519 
10520                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10521                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10522                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10523                      depth_2_ok=depth_1_ok=MagickFalse;
10524                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10525                      depth_1_ok=MagickFalse;
10526                 }
10527 
10528                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10529                   ping_bit_depth=1;
10530 
10531                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10532                   ping_bit_depth=2;
10533 
10534                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10535                   ping_bit_depth=4;
10536               }
10537           }
10538 
10539           image_depth=ping_bit_depth;
10540       }
10541 
10542     else
10543 
10544       if (mng_info->IsPalette)
10545       {
10546         number_colors=image_colors;
10547 
10548         if (image_depth <= 8)
10549           {
10550             /*
10551               Set image palette.
10552             */
10553             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10554 
10555             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10556               {
10557                 for (i=0; i < (ssize_t) number_colors; i++)
10558                 {
10559                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10560                   palette[i].green=
10561                     ScaleQuantumToChar(image->colormap[i].green);
10562                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10563                 }
10564 
10565                 if (logging != MagickFalse)
10566                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10567                     "  Setting up PLTE chunk with %d colors",
10568                     number_colors);
10569 
10570                 ping_have_PLTE=MagickTrue;
10571               }
10572 
10573             /* color_type is PNG_COLOR_TYPE_PALETTE */
10574             if (mng_info->write_png_depth == 0)
10575               {
10576                 size_t
10577                   one;
10578 
10579                 ping_bit_depth=1;
10580                 one=1;
10581 
10582                 while ((one << ping_bit_depth) < (size_t) number_colors)
10583                   ping_bit_depth <<= 1;
10584               }
10585 
10586             ping_num_trans=0;
10587 
10588             if (matte != MagickFalse)
10589               {
10590                 /*
10591                  * Set up trans_colors array.
10592                  */
10593                 assert(number_colors <= 256);
10594 
10595                 ping_num_trans=(unsigned short) (number_transparent +
10596                   number_semitransparent);
10597 
10598                 if (ping_num_trans == 0)
10599                   ping_have_tRNS=MagickFalse;
10600 
10601                 else
10602                   {
10603                     if (logging != MagickFalse)
10604                       {
10605                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10606                           "  Scaling ping_trans_color (1)");
10607                       }
10608                     ping_have_tRNS=MagickTrue;
10609 
10610                     for (i=0; i < ping_num_trans; i++)
10611                     {
10612                        ping_trans_alpha[i]= (png_byte)
10613                          ScaleQuantumToChar(image->colormap[i].alpha);
10614                     }
10615                   }
10616               }
10617           }
10618       }
10619 
10620     else
10621       {
10622 
10623         if (image_depth < 8)
10624           image_depth=8;
10625 
10626         if ((save_image_depth == 16) && (image_depth == 8))
10627           {
10628             if (logging != MagickFalse)
10629               {
10630                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10631                   "    Scaling ping_trans_color from (%d,%d,%d)",
10632                   (int) ping_trans_color.red,
10633                   (int) ping_trans_color.green,
10634                   (int) ping_trans_color.blue);
10635               }
10636 
10637             ping_trans_color.red*=0x0101;
10638             ping_trans_color.green*=0x0101;
10639             ping_trans_color.blue*=0x0101;
10640             ping_trans_color.gray*=0x0101;
10641 
10642             if (logging != MagickFalse)
10643               {
10644                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645                   "    to (%d,%d,%d)",
10646                   (int) ping_trans_color.red,
10647                   (int) ping_trans_color.green,
10648                   (int) ping_trans_color.blue);
10649               }
10650           }
10651       }
10652 
10653     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10654          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10655 
10656     /*
10657       Adjust background and transparency samples in sub-8-bit grayscale files.
10658     */
10659     if (ping_bit_depth < 8 && ping_color_type ==
10660         PNG_COLOR_TYPE_GRAY)
10661       {
10662          png_uint_16
10663            maxval;
10664 
10665          size_t
10666            one=1;
10667 
10668          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10669 
10670          if (ping_exclude_bKGD == MagickFalse)
10671          {
10672 
10673          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10674            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10675            &image->background_color))) +.5)));
10676 
10677          if (logging != MagickFalse)
10678            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10679              "  Setting up bKGD chunk (2)");
10680          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681              "      background_color index is %d",
10682              (int) ping_background.index);
10683 
10684          ping_have_bKGD = MagickTrue;
10685          }
10686 
10687          if (logging != MagickFalse)
10688            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10689              "  Scaling ping_trans_color.gray from %d",
10690              (int)ping_trans_color.gray);
10691 
10692          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10693            ping_trans_color.gray)+.5);
10694 
10695          if (logging != MagickFalse)
10696            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10697              "      to %d", (int)ping_trans_color.gray);
10698       }
10699 
10700   if (ping_exclude_bKGD == MagickFalse)
10701   {
10702     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10703       {
10704         /*
10705            Identify which colormap entry is the background color.
10706         */
10707 
10708         number_colors=image_colors;
10709 
10710         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10711           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10712             break;
10713 
10714         ping_background.index=(png_byte) i;
10715 
10716         if (logging != MagickFalse)
10717           {
10718             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10719               "  Setting up bKGD chunk with index=%d",(int) i);
10720           }
10721 
10722         if (i < (ssize_t) number_colors)
10723           {
10724             ping_have_bKGD = MagickTrue;
10725 
10726             if (logging != MagickFalse)
10727               {
10728                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10729                   "     background   =(%d,%d,%d)",
10730                         (int) ping_background.red,
10731                         (int) ping_background.green,
10732                         (int) ping_background.blue);
10733               }
10734           }
10735 
10736         else  /* Can't happen */
10737           {
10738             if (logging != MagickFalse)
10739               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10740                   "      No room in PLTE to add bKGD color");
10741             ping_have_bKGD = MagickFalse;
10742           }
10743       }
10744   }
10745 
10746   if (logging != MagickFalse)
10747     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10748       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10749       ping_color_type);
10750   /*
10751     Initialize compression level and filtering.
10752   */
10753   if (logging != MagickFalse)
10754     {
10755       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10756         "  Setting up deflate compression");
10757 
10758       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10759         "    Compression buffer size: 32768");
10760     }
10761 
10762   png_set_compression_buffer_size(ping,32768L);
10763 
10764   if (logging != MagickFalse)
10765     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10766       "    Compression mem level: 9");
10767 
10768   png_set_compression_mem_level(ping, 9);
10769 
10770   /* Untangle the "-quality" setting:
10771 
10772      Undefined is 0; the default is used.
10773      Default is 75
10774 
10775      10's digit:
10776 
10777         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10778            zlib default compression level
10779 
10780         1-9: the zlib compression level
10781 
10782      1's digit:
10783 
10784         0-4: the PNG filter method
10785 
10786         5:   libpng adaptive filtering if compression level > 5
10787              libpng filter type "none" if compression level <= 5
10788                 or if image is grayscale or palette
10789 
10790         6:   libpng adaptive filtering
10791 
10792         7:   "LOCO" filtering (intrapixel differing) if writing
10793              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10794              and earlier because of a missing "else".
10795 
10796         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10797              filtering. Unused prior to IM-6.7.0-10, was same as 6
10798 
10799         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10800              Unused prior to IM-6.7.0-10, was same as 6
10801 
10802     Note that using the -quality option, not all combinations of
10803     PNG filter type, zlib compression level, and zlib compression
10804     strategy are possible.  This will be addressed soon in a
10805     release that accomodates "-define png:compression-strategy", etc.
10806 
10807    */
10808 
10809   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10810      image_info->quality;
10811 
10812   if (quality <= 9)
10813     {
10814       if (mng_info->write_png_compression_strategy == 0)
10815         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10816     }
10817 
10818   else if (mng_info->write_png_compression_level == 0)
10819     {
10820       int
10821         level;
10822 
10823       level=(int) MagickMin((ssize_t) quality/10,9);
10824 
10825       mng_info->write_png_compression_level = level+1;
10826     }
10827 
10828   if (mng_info->write_png_compression_strategy == 0)
10829     {
10830         if ((quality %10) == 8 || (quality %10) == 9)
10831 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10832           mng_info->write_png_compression_strategy=Z_RLE+1;
10833 #else
10834           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10835 #endif
10836     }
10837 
10838   if (mng_info->write_png_compression_filter == 0)
10839         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10840 
10841   if (logging != MagickFalse)
10842     {
10843         if (mng_info->write_png_compression_level)
10844           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10845             "    Compression level:    %d",
10846             (int) mng_info->write_png_compression_level-1);
10847 
10848         if (mng_info->write_png_compression_strategy)
10849           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10850             "    Compression strategy: %d",
10851             (int) mng_info->write_png_compression_strategy-1);
10852 
10853         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10854           "  Setting up filtering");
10855 
10856         if (mng_info->write_png_compression_filter == 6)
10857           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10858             "    Base filter method: ADAPTIVE");
10859         else if (mng_info->write_png_compression_filter == 0 ||
10860                  mng_info->write_png_compression_filter == 1)
10861           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10862             "    Base filter method: NONE");
10863         else
10864           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10865             "    Base filter method: %d",
10866             (int) mng_info->write_png_compression_filter-1);
10867     }
10868 
10869   if (mng_info->write_png_compression_level != 0)
10870     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10871 
10872   if (mng_info->write_png_compression_filter == 6)
10873     {
10874       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10875          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10876          (quality < 50))
10877         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10878       else
10879         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10880      }
10881   else if (mng_info->write_png_compression_filter == 7 ||
10882       mng_info->write_png_compression_filter == 10)
10883     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10884 
10885   else if (mng_info->write_png_compression_filter == 8)
10886     {
10887 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10888       if (mng_info->write_mng)
10889       {
10890          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10891              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10892         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10893       }
10894 #endif
10895       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10896     }
10897 
10898   else if (mng_info->write_png_compression_filter == 9)
10899     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10900 
10901   else if (mng_info->write_png_compression_filter != 0)
10902     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10903        mng_info->write_png_compression_filter-1);
10904 
10905   if (mng_info->write_png_compression_strategy != 0)
10906     png_set_compression_strategy(ping,
10907        mng_info->write_png_compression_strategy-1);
10908 
10909   ping_interlace_method=image_info->interlace != NoInterlace;
10910 
10911   if (mng_info->write_mng)
10912     png_set_sig_bytes(ping,8);
10913 
10914   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10915 
10916   if (mng_info->write_png_colortype != 0)
10917     {
10918      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10919        if (ping_have_color != MagickFalse)
10920          {
10921            ping_color_type = PNG_COLOR_TYPE_RGB;
10922 
10923            if (ping_bit_depth < 8)
10924              ping_bit_depth=8;
10925          }
10926 
10927      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10928        if (ping_have_color != MagickFalse)
10929          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10930     }
10931 
10932   if (ping_need_colortype_warning != MagickFalse ||
10933      ((mng_info->write_png_depth &&
10934      (int) mng_info->write_png_depth != ping_bit_depth) ||
10935      (mng_info->write_png_colortype &&
10936      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10937       mng_info->write_png_colortype != 7 &&
10938       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10939     {
10940       if (logging != MagickFalse)
10941         {
10942           if (ping_need_colortype_warning != MagickFalse)
10943             {
10944               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10945                  "  Image has transparency but tRNS chunk was excluded");
10946             }
10947 
10948           if (mng_info->write_png_depth)
10949             {
10950               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10951                   "  Defined png:bit-depth=%u, Computed depth=%u",
10952                   mng_info->write_png_depth,
10953                   ping_bit_depth);
10954             }
10955 
10956           if (mng_info->write_png_colortype)
10957             {
10958               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10959                   "  Defined png:color-type=%u, Computed color type=%u",
10960                   mng_info->write_png_colortype-1,
10961                   ping_color_type);
10962             }
10963         }
10964 
10965       png_warning(ping,
10966         "Cannot write image with defined png:bit-depth or png:color-type.");
10967     }
10968 
10969   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10970     {
10971       /* Add an opaque matte channel */
10972       image->alpha_trait = BlendPixelTrait;
10973       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10974 
10975       if (logging != MagickFalse)
10976         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10977           "  Added an opaque matte channel");
10978     }
10979 
10980   if (number_transparent != 0 || number_semitransparent != 0)
10981     {
10982       if (ping_color_type < 4)
10983         {
10984            ping_have_tRNS=MagickTrue;
10985            if (logging != MagickFalse)
10986              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10987                "  Setting ping_have_tRNS=MagickTrue.");
10988         }
10989     }
10990 
10991   if (logging != MagickFalse)
10992     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10993       "  Writing PNG header chunks");
10994 
10995   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10996                ping_bit_depth,ping_color_type,
10997                ping_interlace_method,ping_compression_method,
10998                ping_filter_method);
10999 
11000   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
11001     {
11002       png_set_PLTE(ping,ping_info,palette,number_colors);
11003 
11004       if (logging != MagickFalse)
11005         {
11006           for (i=0; i< (ssize_t) number_colors; i++)
11007           {
11008             if (i < ping_num_trans)
11009               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11010                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
11011                       (int) i,
11012                       (int) palette[i].red,
11013                       (int) palette[i].green,
11014                       (int) palette[i].blue,
11015                       (int) i,
11016                       (int) ping_trans_alpha[i]);
11017              else
11018               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11019                 "     PLTE[%d] = (%d,%d,%d)",
11020                       (int) i,
11021                       (int) palette[i].red,
11022                       (int) palette[i].green,
11023                       (int) palette[i].blue);
11024            }
11025          }
11026     }
11027 
11028   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
11029   if (ping_exclude_sRGB != MagickFalse ||
11030      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11031   {
11032     if ((ping_exclude_tEXt == MagickFalse ||
11033        ping_exclude_zTXt == MagickFalse) &&
11034        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
11035     {
11036       ResetImageProfileIterator(image);
11037       for (name=GetNextImageProfile(image); name != (char *) NULL; )
11038       {
11039         profile=GetImageProfile(image,name);
11040 
11041         if (profile != (StringInfo *) NULL)
11042           {
11043 #ifdef PNG_WRITE_iCCP_SUPPORTED
11044             if ((LocaleCompare(name,"ICC") == 0) ||
11045                 (LocaleCompare(name,"ICM") == 0))
11046               {
11047                 ping_have_iCCP = MagickTrue;
11048                 if (ping_exclude_iCCP == MagickFalse)
11049                   {
11050                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11051                         "  Setting up iCCP chunk");
11052 
11053                     png_set_iCCP(ping,ping_info,(png_charp) name,0,
11054 #if (PNG_LIBPNG_VER < 10500)
11055                     (png_charp) GetStringInfoDatum(profile),
11056 #else
11057                     (const png_byte *) GetStringInfoDatum(profile),
11058 #endif
11059                     (png_uint_32) GetStringInfoLength(profile));
11060                   }
11061                 else
11062                   {
11063                     /* Do not write hex-encoded ICC chunk */
11064                        name=GetNextImageProfile(image);
11065                        continue;
11066                   }
11067               }
11068 #endif /* WRITE_iCCP */
11069 
11070             if (LocaleCompare(name,"exif") == 0)
11071               {
11072                    /* Do not write hex-encoded ICC chunk; we will
11073                       write it later as an eXIf chunk */
11074                    name=GetNextImageProfile(image);
11075                    continue;
11076               }
11077 
11078               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11079                  "  Setting up zTXt chunk with uuencoded %s profile",
11080                  name);
11081               Magick_png_write_raw_profile(image_info,ping,ping_info,
11082                 (unsigned char *) name,(unsigned char *) name,
11083                 GetStringInfoDatum(profile),
11084                 (png_uint_32) GetStringInfoLength(profile));
11085           }
11086         name=GetNextImageProfile(image);
11087       }
11088     }
11089   }
11090 
11091 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11092   if ((mng_info->have_write_global_srgb == 0) &&
11093       ping_have_iCCP != MagickTrue &&
11094       (ping_have_sRGB != MagickFalse ||
11095       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11096     {
11097       if (ping_exclude_sRGB == MagickFalse)
11098         {
11099           /*
11100             Note image rendering intent.
11101           */
11102           if (logging != MagickFalse)
11103             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11104                 "  Setting up sRGB chunk");
11105 
11106           (void) png_set_sRGB(ping,ping_info,(
11107             Magick_RenderingIntent_to_PNG_RenderingIntent(
11108               image->rendering_intent)));
11109 
11110           ping_have_sRGB = MagickTrue;
11111         }
11112     }
11113 
11114   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11115 #endif
11116     {
11117       if (ping_exclude_gAMA == MagickFalse &&
11118           ping_have_iCCP == MagickFalse &&
11119           ping_have_sRGB == MagickFalse &&
11120           (ping_exclude_sRGB == MagickFalse ||
11121           (image->gamma < .45 || image->gamma > .46)))
11122       {
11123       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11124         {
11125           /*
11126             Note image gamma.
11127             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11128           */
11129           if (logging != MagickFalse)
11130             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11131               "  Setting up gAMA chunk");
11132 
11133           png_set_gAMA(ping,ping_info,image->gamma);
11134         }
11135       }
11136 
11137       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11138         {
11139           if ((mng_info->have_write_global_chrm == 0) &&
11140               (image->chromaticity.red_primary.x != 0.0))
11141             {
11142               /*
11143                 Note image chromaticity.
11144                 Note: if cHRM+gAMA == sRGB write sRGB instead.
11145               */
11146                PrimaryInfo
11147                  bp,
11148                  gp,
11149                  rp,
11150                  wp;
11151 
11152                wp=image->chromaticity.white_point;
11153                rp=image->chromaticity.red_primary;
11154                gp=image->chromaticity.green_primary;
11155                bp=image->chromaticity.blue_primary;
11156 
11157                if (logging != MagickFalse)
11158                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11159                    "  Setting up cHRM chunk");
11160 
11161                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11162                    bp.x,bp.y);
11163            }
11164         }
11165     }
11166 
11167   if (ping_exclude_bKGD == MagickFalse)
11168     {
11169       if (ping_have_bKGD != MagickFalse)
11170         {
11171           png_set_bKGD(ping,ping_info,&ping_background);
11172           if (logging != MagickFalse)
11173             {
11174               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11175                    "    Setting up bKGD chunk");
11176               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11177                    "      background color = (%d,%d,%d)",
11178                         (int) ping_background.red,
11179                         (int) ping_background.green,
11180                         (int) ping_background.blue);
11181               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182                    "      index = %d, gray=%d",
11183                         (int) ping_background.index,
11184                         (int) ping_background.gray);
11185             }
11186          }
11187     }
11188 
11189   if (ping_exclude_pHYs == MagickFalse)
11190     {
11191       if (ping_have_pHYs != MagickFalse)
11192         {
11193           png_set_pHYs(ping,ping_info,
11194              ping_pHYs_x_resolution,
11195              ping_pHYs_y_resolution,
11196              ping_pHYs_unit_type);
11197 
11198           if (logging != MagickFalse)
11199             {
11200               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11201                    "    Setting up pHYs chunk");
11202               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11203                    "      x_resolution=%lu",
11204                    (unsigned long) ping_pHYs_x_resolution);
11205               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11206                    "      y_resolution=%lu",
11207                    (unsigned long) ping_pHYs_y_resolution);
11208               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11209                    "      unit_type=%lu",
11210                    (unsigned long) ping_pHYs_unit_type);
11211             }
11212         }
11213     }
11214 
11215 #if defined(PNG_tIME_SUPPORTED)
11216   if (ping_exclude_tIME == MagickFalse)
11217     {
11218       const char
11219         *timestamp;
11220 
11221       if (image->taint == MagickFalse)
11222         {
11223           timestamp=GetImageOption(image_info,"png:tIME");
11224 
11225           if (timestamp == (const char *) NULL)
11226             timestamp=GetImageProperty(image,"png:tIME",exception);
11227         }
11228 
11229       else
11230         {
11231           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11232              "  Reset tIME in tainted image");
11233 
11234           timestamp=GetImageProperty(image,"date:modify",exception);
11235         }
11236 
11237       if (timestamp != (const char *) NULL)
11238           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11239     }
11240 #endif
11241 
11242   if (mng_info->need_blob != MagickFalse)
11243   {
11244     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11245        MagickFalse)
11246        png_error(ping,"WriteBlob Failed");
11247 
11248      ping_have_blob=MagickTrue;
11249   }
11250 
11251   png_write_info_before_PLTE(ping, ping_info);
11252 
11253   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11254     {
11255       if (logging != MagickFalse)
11256         {
11257           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11258               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11259         }
11260 
11261       if (ping_color_type == 3)
11262          (void) png_set_tRNS(ping, ping_info,
11263                 ping_trans_alpha,
11264                 ping_num_trans,
11265                 NULL);
11266 
11267       else
11268         {
11269            (void) png_set_tRNS(ping, ping_info,
11270                   NULL,
11271                   0,
11272                   &ping_trans_color);
11273 
11274            if (logging != MagickFalse)
11275              {
11276                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11277                  "     tRNS color   =(%d,%d,%d)",
11278                        (int) ping_trans_color.red,
11279                        (int) ping_trans_color.green,
11280                        (int) ping_trans_color.blue);
11281              }
11282          }
11283     }
11284 
11285   png_write_info(ping,ping_info);
11286 
11287   /* write orNT if image->orientation is defined */
11288   if (image->orientation != UndefinedOrientation)
11289     {
11290       unsigned char
11291         chunk[6];
11292       (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11293       PNGType(chunk,mng_orNT);
11294       LogPNGChunk(logging,mng_orNT,1L);
11295       /* PNG uses Exif orientation values */
11296       chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11297       (void) WriteBlob(image,5,chunk);
11298       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11299     }
11300 
11301   ping_wrote_caNv = MagickFalse;
11302 
11303   /* write caNv chunk */
11304   if (ping_exclude_caNv == MagickFalse)
11305     {
11306       if ((image->page.width != 0 && image->page.width != image->columns) ||
11307           (image->page.height != 0 && image->page.height != image->rows) ||
11308           image->page.x != 0 || image->page.y != 0)
11309         {
11310           unsigned char
11311             chunk[20];
11312 
11313           (void) WriteBlobMSBULong(image,16L);  /* data length=8 */
11314           PNGType(chunk,mng_caNv);
11315           LogPNGChunk(logging,mng_caNv,16L);
11316           PNGLong(chunk+4,(png_uint_32) image->page.width);
11317           PNGLong(chunk+8,(png_uint_32) image->page.height);
11318           PNGsLong(chunk+12,(png_int_32) image->page.x);
11319           PNGsLong(chunk+16,(png_int_32) image->page.y);
11320           (void) WriteBlob(image,20,chunk);
11321           (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11322           ping_wrote_caNv = MagickTrue;
11323         }
11324     }
11325 
11326 #if defined(PNG_oFFs_SUPPORTED)
11327   if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11328     {
11329       if (image->page.x || image->page.y)
11330         {
11331            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11332               (png_int_32) image->page.y, 0);
11333 
11334            if (logging != MagickFalse)
11335              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11336                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
11337                  (int) image->page.x, (int) image->page.y);
11338         }
11339     }
11340 #endif
11341 
11342 #if (PNG_LIBPNG_VER == 10206)
11343     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11344 #define PNG_HAVE_IDAT               0x04
11345     ping->mode |= PNG_HAVE_IDAT;
11346 #undef PNG_HAVE_IDAT
11347 #endif
11348 
11349   png_set_packing(ping);
11350   /*
11351     Allocate memory.
11352   */
11353   rowbytes=image->columns;
11354   if (image_depth > 8)
11355     rowbytes*=2;
11356   switch (ping_color_type)
11357     {
11358       case PNG_COLOR_TYPE_RGB:
11359         rowbytes*=3;
11360         break;
11361 
11362       case PNG_COLOR_TYPE_GRAY_ALPHA:
11363         rowbytes*=2;
11364         break;
11365 
11366       case PNG_COLOR_TYPE_RGBA:
11367         rowbytes*=4;
11368         break;
11369 
11370       default:
11371         break;
11372     }
11373 
11374   if (logging != MagickFalse)
11375     {
11376       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11377         "  Writing PNG image data");
11378 
11379       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11380         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11381     }
11382   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11383   if (pixel_info == (MemoryInfo *) NULL)
11384     png_error(ping,"Allocation of memory for pixels failed");
11385   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11386   (void) memset(ping_pixels,0,rowbytes*sizeof(*ping_pixels));
11387   /*
11388     Initialize image scanlines.
11389   */
11390   quantum_info=AcquireQuantumInfo(image_info,image);
11391   if (quantum_info == (QuantumInfo *) NULL)
11392     png_error(ping,"Memory allocation for quantum_info failed");
11393   quantum_info->format=UndefinedQuantumFormat;
11394   SetQuantumDepth(image,quantum_info,image_depth);
11395   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11396   num_passes=png_set_interlace_handling(ping);
11397 
11398   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11399        !mng_info->write_png48 && !mng_info->write_png64 &&
11400        !mng_info->write_png32) &&
11401        (mng_info->IsPalette ||
11402        (image_info->type == BilevelType)) &&
11403        image_matte == MagickFalse &&
11404        ping_have_non_bw == MagickFalse)
11405     {
11406       /* Palette, Bilevel, or Opaque Monochrome */
11407       QuantumType
11408         quantum_type;
11409 
11410       register const Quantum
11411         *p;
11412 
11413       quantum_type=RedQuantum;
11414       if (mng_info->IsPalette)
11415         {
11416           quantum_type=GrayQuantum;
11417           if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE)
11418             quantum_type=IndexQuantum;
11419         }
11420       SetQuantumDepth(image,quantum_info,8);
11421       for (pass=0; pass < num_passes; pass++)
11422       {
11423         /*
11424           Convert PseudoClass image to a PNG monochrome image.
11425         */
11426         for (y=0; y < (ssize_t) image->rows; y++)
11427         {
11428           if (logging != MagickFalse && y == 0)
11429              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11430                  "    Writing row of pixels (0)");
11431 
11432           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11433 
11434           if (p == (const Quantum *) NULL)
11435             break;
11436 
11437           (void) ExportQuantumPixels(image,(CacheView *) NULL,
11438             quantum_info,quantum_type,ping_pixels,exception);
11439           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11440             for (i=0; i < (ssize_t) image->columns; i++)
11441                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11442                       255 : 0);
11443 
11444           if (logging != MagickFalse && y == 0)
11445             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11446                 "    Writing row of pixels (1)");
11447 
11448           png_write_row(ping,ping_pixels);
11449 
11450           status=SetImageProgress(image,SaveImageTag,
11451               (MagickOffsetType) (pass * image->rows + y),
11452               num_passes * image->rows);
11453 
11454           if (status == MagickFalse)
11455             break;
11456         }
11457       }
11458     }
11459 
11460   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11461     {
11462       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11463           !mng_info->write_png48 && !mng_info->write_png64 &&
11464           !mng_info->write_png32) && (image_matte != MagickFalse ||
11465           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11466           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11467         {
11468           register const Quantum
11469             *p;
11470 
11471           for (pass=0; pass < num_passes; pass++)
11472           {
11473 
11474           for (y=0; y < (ssize_t) image->rows; y++)
11475           {
11476             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11477 
11478             if (p == (const Quantum *) NULL)
11479               break;
11480 
11481             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11482               {
11483                 if (mng_info->IsPalette)
11484                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11485                     quantum_info,GrayQuantum,ping_pixels,exception);
11486 
11487                 else
11488                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11489                     quantum_info,RedQuantum,ping_pixels,exception);
11490 
11491                 if (logging != MagickFalse && y == 0)
11492                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11493                        "    Writing GRAY PNG pixels (2)");
11494               }
11495 
11496             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11497               {
11498                 if (logging != MagickFalse && y == 0)
11499                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11500                          "    Writing GRAY_ALPHA PNG pixels (2)");
11501 
11502                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11503                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11504               }
11505 
11506             if (logging != MagickFalse && y == 0)
11507               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11508                   "    Writing row of pixels (2)");
11509 
11510             png_write_row(ping,ping_pixels);
11511 
11512             status=SetImageProgress(image,SaveImageTag,
11513               (MagickOffsetType) (pass * image->rows + y),
11514               num_passes * image->rows);
11515 
11516             if (status == MagickFalse)
11517               break;
11518             }
11519           }
11520         }
11521 
11522       else
11523         {
11524           register const Quantum
11525             *p;
11526 
11527           for (pass=0; pass < num_passes; pass++)
11528           {
11529             if ((image_depth > 8) ||
11530                 mng_info->write_png24 ||
11531                 mng_info->write_png32 ||
11532                 mng_info->write_png48 ||
11533                 mng_info->write_png64 ||
11534                 (!mng_info->write_png8 && !mng_info->IsPalette))
11535             {
11536               for (y=0; y < (ssize_t) image->rows; y++)
11537               {
11538                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11539 
11540                 if (p == (const Quantum *) NULL)
11541                   break;
11542 
11543                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11544                   {
11545                     if (image->storage_class == DirectClass)
11546                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11547                         quantum_info,RedQuantum,ping_pixels,exception);
11548 
11549                     else
11550                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11551                         quantum_info,GrayQuantum,ping_pixels,exception);
11552                   }
11553 
11554                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11555                   {
11556                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11557                       quantum_info,GrayAlphaQuantum,ping_pixels,
11558                       exception);
11559 
11560                     if (logging != MagickFalse && y == 0)
11561                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11562                            "    Writing GRAY_ALPHA PNG pixels (3)");
11563                   }
11564 
11565                 else if (image_matte != MagickFalse)
11566                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11567                     quantum_info,RGBAQuantum,ping_pixels,exception);
11568 
11569                 else
11570                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11571                     quantum_info,RGBQuantum,ping_pixels,exception);
11572 
11573                 if (logging != MagickFalse && y == 0)
11574                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575                       "    Writing row of pixels (3)");
11576 
11577                 png_write_row(ping,ping_pixels);
11578 
11579                 status=SetImageProgress(image,SaveImageTag,
11580                   (MagickOffsetType) (pass * image->rows + y),
11581                   num_passes * image->rows);
11582 
11583                 if (status == MagickFalse)
11584                   break;
11585               }
11586             }
11587 
11588           else
11589             /* not ((image_depth > 8) ||
11590                 mng_info->write_png24 || mng_info->write_png32 ||
11591                 mng_info->write_png48 || mng_info->write_png64 ||
11592                 (!mng_info->write_png8 && !mng_info->IsPalette))
11593              */
11594             {
11595               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11596                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11597                 {
11598                   if (logging != MagickFalse)
11599                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11600                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11601 
11602                   SetQuantumDepth(image,quantum_info,8);
11603                   image_depth=8;
11604                 }
11605 
11606               for (y=0; y < (ssize_t) image->rows; y++)
11607               {
11608                 if (logging != MagickFalse && y == 0)
11609                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11610                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11611                     pass);
11612 
11613                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11614 
11615                 if (p == (const Quantum *) NULL)
11616                   break;
11617 
11618                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11619                   {
11620                     SetQuantumDepth(image,quantum_info,image->depth);
11621 
11622                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11623                        quantum_info,GrayQuantum,ping_pixels,exception);
11624                   }
11625 
11626                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11627                   {
11628                     if (logging != MagickFalse && y == 0)
11629                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11630                            "  Writing GRAY_ALPHA PNG pixels (4)");
11631 
11632                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11633                          quantum_info,GrayAlphaQuantum,ping_pixels,
11634                          exception);
11635                   }
11636 
11637                 else
11638                   {
11639                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11640                       quantum_info,IndexQuantum,ping_pixels,exception);
11641 
11642                     if (logging != MagickFalse && y <= 2)
11643                     {
11644                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11645                           "  Writing row of non-gray pixels (4)");
11646 
11647                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11648                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11649                           (int)ping_pixels[0],(int)ping_pixels[1]);
11650                     }
11651                   }
11652                 png_write_row(ping,ping_pixels);
11653 
11654                 status=SetImageProgress(image,SaveImageTag,
11655                   (MagickOffsetType) (pass * image->rows + y),
11656                   num_passes * image->rows);
11657 
11658                 if (status == MagickFalse)
11659                   break;
11660               }
11661             }
11662           }
11663         }
11664     }
11665 
11666   if (quantum_info != (QuantumInfo *) NULL)
11667     quantum_info=DestroyQuantumInfo(quantum_info);
11668 
11669   if (logging != MagickFalse)
11670     {
11671       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11672         "  Wrote PNG image data");
11673 
11674       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11675         "    Width: %.20g",(double) ping_width);
11676 
11677       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11678         "    Height: %.20g",(double) ping_height);
11679 
11680       if (mng_info->write_png_depth)
11681         {
11682           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11683             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11684         }
11685 
11686       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11687         "    PNG bit-depth written: %d",ping_bit_depth);
11688 
11689       if (mng_info->write_png_colortype)
11690         {
11691           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11692             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11693         }
11694 
11695       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11696         "    PNG color-type written: %d",ping_color_type);
11697 
11698       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11699         "    PNG Interlace method: %d",ping_interlace_method);
11700     }
11701   /*
11702     Generate text chunks after IDAT.
11703   */
11704   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11705   {
11706     ResetImagePropertyIterator(image);
11707     property=GetNextImageProperty(image);
11708     while (property != (const char *) NULL)
11709     {
11710       png_textp
11711         text;
11712 
11713       value=GetImageProperty(image,property,exception);
11714 
11715       /* Don't write any "png:" or "jpeg:" properties; those are just for
11716        * "identify" or for passing through to another JPEG
11717        */
11718       if ((LocaleNCompare(property,"png:",4) != 0 &&
11719            LocaleNCompare(property,"jpeg:",5) != 0) &&
11720 
11721 
11722           /* Suppress density and units if we wrote a pHYs chunk */
11723           (ping_exclude_pHYs != MagickFalse      ||
11724           LocaleCompare(property,"density") != 0 ||
11725           LocaleCompare(property,"units") != 0) &&
11726 
11727           /* Suppress the IM-generated Date:create and Date:modify */
11728           (ping_exclude_date == MagickFalse      ||
11729           LocaleNCompare(property, "Date:",5) != 0))
11730         {
11731         if (value != (const char *) NULL)
11732           {
11733 
11734 #if PNG_LIBPNG_VER >= 10400
11735             text=(png_textp) png_malloc(ping,
11736                  (png_alloc_size_t) sizeof(png_text));
11737 #else
11738             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11739 #endif
11740             text[0].key=(char *) property;
11741             text[0].text=(char *) value;
11742             text[0].text_length=strlen(value);
11743 
11744             if (ping_exclude_tEXt != MagickFalse)
11745                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11746 
11747             else if (ping_exclude_zTXt != MagickFalse)
11748                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11749 
11750             else
11751             {
11752                text[0].compression=image_info->compression == NoCompression ||
11753                  (image_info->compression == UndefinedCompression &&
11754                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11755                  PNG_TEXT_COMPRESSION_zTXt ;
11756             }
11757 
11758             if (logging != MagickFalse)
11759               {
11760                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11761                   "  Setting up text chunk");
11762 
11763                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11764                   "    keyword: '%s'",text[0].key);
11765               }
11766 
11767             png_set_text(ping,ping_info,text,1);
11768             png_free(ping,text);
11769           }
11770         }
11771       property=GetNextImageProperty(image);
11772     }
11773   }
11774 
11775   /* write eXIf profile */
11776   if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11777     {
11778       ResetImageProfileIterator(image);
11779 
11780       for (name=GetNextImageProfile(image); name != (char *) NULL; )
11781       {
11782         if (LocaleCompare(name,"exif") == 0)
11783           {
11784             profile=GetImageProfile(image,name);
11785 
11786             if (profile != (StringInfo *) NULL)
11787               {
11788                 png_uint_32
11789                   length;
11790 
11791                 unsigned char
11792                   chunk[4],
11793                   *data;
11794 
11795                 StringInfo
11796                   *ping_profile;
11797 
11798                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11799                     "  Have eXIf profile");
11800 
11801                 ping_profile=CloneStringInfo(profile);
11802                 data=GetStringInfoDatum(ping_profile),
11803                 length=(png_uint_32) GetStringInfoLength(ping_profile);
11804 
11805                 PNGType(chunk,mng_eXIf);
11806                 if (length < 7)
11807                   {
11808                     ping_profile=DestroyStringInfo(ping_profile);
11809                     break;  /* otherwise crashes */
11810                   }
11811 
11812                 if (*data == 'E' && *(data+1) == 'x' && *(data+2) == 'i' &&
11813                     *(data+3) == 'f' && *(data+4) == '\0' && *(data+5) == '\0')
11814                   {
11815                     /* skip the "Exif\0\0" JFIF Exif Header ID */
11816                     length -= 6;
11817                     data += 6;
11818                   }
11819 
11820                 LogPNGChunk(logging,chunk,length);
11821                 (void) WriteBlobMSBULong(image,length);
11822                 (void) WriteBlob(image,4,chunk);
11823                 (void) WriteBlob(image,length,data);
11824                 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4), data,
11825                   (uInt) length));
11826                 ping_profile=DestroyStringInfo(ping_profile);
11827                 break;
11828              }
11829          }
11830        name=GetNextImageProfile(image);
11831      }
11832   }
11833 
11834   if (logging != MagickFalse)
11835     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11836       "  Writing PNG end info");
11837 
11838   png_write_end(ping,ping_info);
11839 
11840   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11841     {
11842       if (mng_info->page.x || mng_info->page.y ||
11843           (ping_width != mng_info->page.width) ||
11844           (ping_height != mng_info->page.height))
11845         {
11846           unsigned char
11847             chunk[32];
11848 
11849           /*
11850             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11851           */
11852           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11853           PNGType(chunk,mng_FRAM);
11854           LogPNGChunk(logging,mng_FRAM,27L);
11855           chunk[4]=4;
11856           chunk[5]=0;  /* frame name separator (no name) */
11857           chunk[6]=1;  /* flag for changing delay, for next frame only */
11858           chunk[7]=0;  /* flag for changing frame timeout */
11859           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11860           chunk[9]=0;  /* flag for changing frame sync_id */
11861           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11862           chunk[14]=0; /* clipping boundaries delta type */
11863           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11864           PNGLong(chunk+19,
11865              (png_uint_32) (mng_info->page.x + ping_width));
11866           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11867           PNGLong(chunk+27,
11868              (png_uint_32) (mng_info->page.y + ping_height));
11869           (void) WriteBlob(image,31,chunk);
11870           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11871           mng_info->old_framing_mode=4;
11872           mng_info->framing_mode=1;
11873         }
11874 
11875       else
11876         mng_info->framing_mode=3;
11877     }
11878   if (mng_info->write_mng && !mng_info->need_fram &&
11879       ((int) image->dispose == 3))
11880      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11881 
11882   /*
11883     Free PNG resources.
11884   */
11885 
11886   png_destroy_write_struct(&ping,&ping_info);
11887 
11888   pixel_info=RelinquishVirtualMemory(pixel_info);
11889 
11890   if (ping_have_blob != MagickFalse)
11891      (void) CloseBlob(image);
11892 
11893   image_info=DestroyImageInfo(image_info);
11894   image=DestroyImage(image);
11895 
11896   /* Store bit depth actually written */
11897   s[0]=(char) ping_bit_depth;
11898   s[1]='\0';
11899 
11900   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11901 
11902   if (logging != MagickFalse)
11903     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11904       "  exit WriteOnePNGImage()");
11905 
11906 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11907   UnlockSemaphoreInfo(ping_semaphore);
11908 #endif
11909 
11910    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11911     *    Throwing an Exception when an error occurs.
11912     */
11913 
11914   return(MagickTrue);
11915 /*  End write one PNG image */
11916 
11917 }
11918 
11919 /*
11920 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11921 %                                                                             %
11922 %                                                                             %
11923 %                                                                             %
11924 %   W r i t e P N G I m a g e                                                 %
11925 %                                                                             %
11926 %                                                                             %
11927 %                                                                             %
11928 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11929 %
11930 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11931 %  Multiple-image Network Graphics (MNG) image file.
11932 %
11933 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11934 %
11935 %  The format of the WritePNGImage method is:
11936 %
11937 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11938 %        Image *image,ExceptionInfo *exception)
11939 %
11940 %  A description of each parameter follows:
11941 %
11942 %    o image_info: the image info.
11943 %
11944 %    o image:  The image.
11945 %
11946 %    o exception: return any errors or warnings in this structure.
11947 %
11948 %  Returns MagickTrue on success, MagickFalse on failure.
11949 %
11950 %  Communicating with the PNG encoder:
11951 %
11952 %  While the datastream written is always in PNG format and normally would
11953 %  be given the "png" file extension, this method also writes the following
11954 %  pseudo-formats which are subsets of png:
11955 %
11956 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11957 %               a depth greater than 8, the depth is reduced. If transparency
11958 %               is present, the tRNS chunk must only have values 0 and 255
11959 %               (i.e., transparency is binary: fully opaque or fully
11960 %               transparent).  If other values are present they will be
11961 %               50%-thresholded to binary transparency.  If more than 256
11962 %               colors are present, they will be quantized to the 4-4-4-1,
11963 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11964 %               of any resulting fully-transparent pixels is changed to
11965 %               the image's background color.
11966 %
11967 %               If you want better quantization or dithering of the colors
11968 %               or alpha than that, you need to do it before calling the
11969 %               PNG encoder. The pixels contain 8-bit indices even if
11970 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11971 %               images will be written as indexed PNG files even though the
11972 %               PNG grayscale type might be slightly more efficient.  Please
11973 %               note that writing to the PNG8 format may result in loss
11974 %               of color and alpha data.
11975 %
11976 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11977 %               chunk can be present to convey binary transparency by naming
11978 %               one of the colors as transparent.  The only loss incurred
11979 %               is reduction of sample depth to 8.  If the image has more
11980 %               than one transparent color, has semitransparent pixels, or
11981 %               has an opaque pixel with the same RGB components as the
11982 %               transparent color, an image is not written.
11983 %
11984 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11985 %               transparency is permitted, i.e., the alpha sample for
11986 %               each pixel can have any value from 0 to 255. The alpha
11987 %               channel is present even if the image is fully opaque.
11988 %               The only loss in data is the reduction of the sample depth
11989 %               to 8.
11990 %
11991 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11992 %               chunk can be present to convey binary transparency by naming
11993 %               one of the colors as transparent.  If the image has more
11994 %               than one transparent color, has semitransparent pixels, or
11995 %               has an opaque pixel with the same RGB components as the
11996 %               transparent color, an image is not written.
11997 %
11998 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11999 %               transparency is permitted, i.e., the alpha sample for
12000 %               each pixel can have any value from 0 to 65535. The alpha
12001 %               channel is present even if the image is fully opaque.
12002 %
12003 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
12004 %               image, if the input was a PNG, is written.  If these values
12005 %               cannot be found, or if the pixels have been changed in a way
12006 %               that makes this impossible, then "PNG00" falls back to the
12007 %               regular "PNG" format.
12008 %
12009 %    o -define: For more precise control of the PNG output, you can use the
12010 %               Image options "png:bit-depth" and "png:color-type".  These
12011 %               can be set from the commandline with "-define" and also
12012 %               from the application programming interfaces.  The options
12013 %               are case-independent and are converted to lowercase before
12014 %               being passed to this encoder.
12015 %
12016 %               png:color-type can be 0, 2, 3, 4, or 6.
12017 %
12018 %               When png:color-type is 0 (Grayscale), png:bit-depth can
12019 %               be 1, 2, 4, 8, or 16.
12020 %
12021 %               When png:color-type is 2 (RGB), png:bit-depth can
12022 %               be 8 or 16.
12023 %
12024 %               When png:color-type is 3 (Indexed), png:bit-depth can
12025 %               be 1, 2, 4, or 8.  This refers to the number of bits
12026 %               used to store the index.  The color samples always have
12027 %               bit-depth 8 in indexed PNG files.
12028 %
12029 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12030 %               png:bit-depth can be 8 or 16.
12031 %
12032 %               If the image cannot be written without loss with the
12033 %               requested bit-depth and color-type, a PNG file will not
12034 %               be written, a warning will be issued, and the encoder will
12035 %               return MagickFalse.
12036 %
12037 %  Since image encoders should not be responsible for the "heavy lifting",
12038 %  the user should make sure that ImageMagick has already reduced the
12039 %  image depth and number of colors and limit transparency to binary
12040 %  transparency prior to attempting to write the image with depth, color,
12041 %  or transparency limitations.
12042 %
12043 %  Note that another definition, "png:bit-depth-written" exists, but it
12044 %  is not intended for external use.  It is only used internally by the
12045 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12046 %
12047 %  As of version 6.6.6 the following optimizations are always done:
12048 %
12049 %   o  32-bit depth is reduced to 16.
12050 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
12051 %      high byte and low byte are identical.
12052 %   o  Palette is sorted to remove unused entries and to put a
12053 %      transparent color first, if BUILD_PNG_PALETTE is defined.
12054 %   o  Opaque matte channel is removed when writing an indexed PNG.
12055 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
12056 %      this can be done without loss and a larger bit depth N was not
12057 %      requested via the "-define png:bit-depth=N" option.
12058 %   o  If matte channel is present but only one transparent color is
12059 %      present, RGB+tRNS is written instead of RGBA
12060 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
12061 %      was requested when converting an opaque image).
12062 %
12063 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12064 */
WritePNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12065 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12066   Image *image,ExceptionInfo *exception)
12067 {
12068   MagickBooleanType
12069     excluding,
12070     logging,
12071     status;
12072 
12073   MngInfo
12074     *mng_info;
12075 
12076   const char
12077     *value;
12078 
12079   int
12080     source;
12081 
12082   /*
12083     Open image file.
12084   */
12085   assert(image_info != (const ImageInfo *) NULL);
12086   assert(image_info->signature == MagickCoreSignature);
12087   assert(image != (Image *) NULL);
12088   assert(image->signature == MagickCoreSignature);
12089   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12090   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12091   /*
12092     Allocate a MngInfo structure.
12093   */
12094   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12095 
12096   if (mng_info == (MngInfo *) NULL)
12097     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12098 
12099   /*
12100     Initialize members of the MngInfo structure.
12101   */
12102   (void) memset(mng_info,0,sizeof(MngInfo));
12103   mng_info->image=image;
12104   mng_info->equal_backgrounds=MagickTrue;
12105 
12106   /* See if user has requested a specific PNG subformat */
12107 
12108   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12109   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12110   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12111   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12112   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12113 
12114   value=GetImageOption(image_info,"png:format");
12115 
12116   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12117     {
12118       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12119          "  Format=%s",value);
12120 
12121       mng_info->write_png8 = MagickFalse;
12122       mng_info->write_png24 = MagickFalse;
12123       mng_info->write_png32 = MagickFalse;
12124       mng_info->write_png48 = MagickFalse;
12125       mng_info->write_png64 = MagickFalse;
12126 
12127       if (LocaleCompare(value,"png8") == 0)
12128         mng_info->write_png8 = MagickTrue;
12129 
12130       else if (LocaleCompare(value,"png24") == 0)
12131         mng_info->write_png24 = MagickTrue;
12132 
12133       else if (LocaleCompare(value,"png32") == 0)
12134         mng_info->write_png32 = MagickTrue;
12135 
12136       else if (LocaleCompare(value,"png48") == 0)
12137         mng_info->write_png48 = MagickTrue;
12138 
12139       else if (LocaleCompare(value,"png64") == 0)
12140         mng_info->write_png64 = MagickTrue;
12141 
12142       else if ((LocaleCompare(value,"png00") == 0) ||
12143          LocaleCompare(image_info->magick,"PNG00") == 0)
12144         {
12145           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12146           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12147 
12148           if (value != (char *) NULL)
12149             {
12150               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12151                  "  png00 inherited bit depth=%s",value);
12152 
12153               if (LocaleCompare(value,"1") == 0)
12154                 mng_info->write_png_depth = 1;
12155 
12156               else if (LocaleCompare(value,"2") == 0)
12157                 mng_info->write_png_depth = 2;
12158 
12159               else if (LocaleCompare(value,"4") == 0)
12160                 mng_info->write_png_depth = 4;
12161 
12162               else if (LocaleCompare(value,"8") == 0)
12163                 mng_info->write_png_depth = 8;
12164 
12165               else if (LocaleCompare(value,"16") == 0)
12166                 mng_info->write_png_depth = 16;
12167             }
12168 
12169           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12170 
12171           if (value != (char *) NULL)
12172             {
12173               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12174                  "  png00 inherited color type=%s",value);
12175 
12176               if (LocaleCompare(value,"0") == 0)
12177                 mng_info->write_png_colortype = 1;
12178 
12179               else if (LocaleCompare(value,"2") == 0)
12180                 mng_info->write_png_colortype = 3;
12181 
12182               else if (LocaleCompare(value,"3") == 0)
12183                 mng_info->write_png_colortype = 4;
12184 
12185               else if (LocaleCompare(value,"4") == 0)
12186                 mng_info->write_png_colortype = 5;
12187 
12188               else if (LocaleCompare(value,"6") == 0)
12189                 mng_info->write_png_colortype = 7;
12190             }
12191         }
12192     }
12193 
12194   if (mng_info->write_png8)
12195     {
12196       mng_info->write_png_colortype = /* 3 */ 4;
12197       mng_info->write_png_depth = 8;
12198       image->depth = 8;
12199     }
12200 
12201   if (mng_info->write_png24)
12202     {
12203       mng_info->write_png_colortype = /* 2 */ 3;
12204       mng_info->write_png_depth = 8;
12205       image->depth = 8;
12206 
12207       if (image->alpha_trait != UndefinedPixelTrait)
12208         (void) SetImageType(image,TrueColorAlphaType,exception);
12209 
12210       else
12211         (void) SetImageType(image,TrueColorType,exception);
12212 
12213       (void) SyncImage(image,exception);
12214     }
12215 
12216   if (mng_info->write_png32)
12217     {
12218       mng_info->write_png_colortype = /* 6 */  7;
12219       mng_info->write_png_depth = 8;
12220       image->depth = 8;
12221       image->alpha_trait = BlendPixelTrait;
12222 
12223       (void) SetImageType(image,TrueColorAlphaType,exception);
12224       (void) SyncImage(image,exception);
12225     }
12226 
12227   if (mng_info->write_png48)
12228     {
12229       mng_info->write_png_colortype = /* 2 */ 3;
12230       mng_info->write_png_depth = 16;
12231       image->depth = 16;
12232 
12233       if (image->alpha_trait != UndefinedPixelTrait)
12234         (void) SetImageType(image,TrueColorAlphaType,exception);
12235 
12236       else
12237         (void) SetImageType(image,TrueColorType,exception);
12238 
12239       (void) SyncImage(image,exception);
12240     }
12241 
12242   if (mng_info->write_png64)
12243     {
12244       mng_info->write_png_colortype = /* 6 */  7;
12245       mng_info->write_png_depth = 16;
12246       image->depth = 16;
12247       image->alpha_trait = BlendPixelTrait;
12248 
12249       (void) SetImageType(image,TrueColorAlphaType,exception);
12250       (void) SyncImage(image,exception);
12251     }
12252 
12253   value=GetImageOption(image_info,"png:bit-depth");
12254 
12255   if (value != (char *) NULL)
12256     {
12257       if (LocaleCompare(value,"1") == 0)
12258         mng_info->write_png_depth = 1;
12259 
12260       else if (LocaleCompare(value,"2") == 0)
12261         mng_info->write_png_depth = 2;
12262 
12263       else if (LocaleCompare(value,"4") == 0)
12264         mng_info->write_png_depth = 4;
12265 
12266       else if (LocaleCompare(value,"8") == 0)
12267         mng_info->write_png_depth = 8;
12268 
12269       else if (LocaleCompare(value,"16") == 0)
12270         mng_info->write_png_depth = 16;
12271 
12272       else
12273         (void) ThrowMagickException(exception,
12274              GetMagickModule(),CoderWarning,
12275              "ignoring invalid defined png:bit-depth",
12276              "=%s",value);
12277 
12278       if (logging != MagickFalse)
12279         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12280           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12281     }
12282 
12283   value=GetImageOption(image_info,"png:color-type");
12284 
12285   if (value != (char *) NULL)
12286     {
12287       /* We must store colortype+1 because 0 is a valid colortype */
12288       if (LocaleCompare(value,"0") == 0)
12289         mng_info->write_png_colortype = 1;
12290 
12291       else if (LocaleCompare(value,"1") == 0)
12292         mng_info->write_png_colortype = 2;
12293 
12294       else if (LocaleCompare(value,"2") == 0)
12295         mng_info->write_png_colortype = 3;
12296 
12297       else if (LocaleCompare(value,"3") == 0)
12298         mng_info->write_png_colortype = 4;
12299 
12300       else if (LocaleCompare(value,"4") == 0)
12301         mng_info->write_png_colortype = 5;
12302 
12303       else if (LocaleCompare(value,"6") == 0)
12304         mng_info->write_png_colortype = 7;
12305 
12306       else
12307         (void) ThrowMagickException(exception,
12308              GetMagickModule(),CoderWarning,
12309              "ignoring invalid defined png:color-type",
12310              "=%s",value);
12311 
12312       if (logging != MagickFalse)
12313         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12314           "  png:color-type=%d was defined.\n",
12315           mng_info->write_png_colortype-1);
12316     }
12317 
12318   /* Check for chunks to be excluded:
12319    *
12320    * The default is to not exclude any known chunks except for any
12321    * listed in the "unused_chunks" array, above.
12322    *
12323    * Chunks can be listed for exclusion via a "png:exclude-chunk"
12324    * define (in the image properties or in the image artifacts)
12325    * or via a mng_info member.  For convenience, in addition
12326    * to or instead of a comma-separated list of chunks, the
12327    * "exclude-chunk" string can be simply "all" or "none".
12328    *
12329    * Note that the "-strip" option provides a convenient way of
12330    * doing the equivalent of
12331    *
12332    *    -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12333    *            iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12334    *
12335    * The exclude-chunk define takes priority over the mng_info.
12336    *
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
12345    * artifacts.
12346    *
12347    * Finally, all chunks listed in the "unused_chunks" array are
12348    * automatically excluded, regardless of the other instructions
12349    * or lack thereof.
12350    *
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).
12354    *
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).
12357    *
12358    * The -strip option causes StripImage() to set the png:include-chunk
12359    * artifact to "none,trns,gama".
12360    */
12361 
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_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12378   mng_info->ping_exclude_zTXt=MagickFalse;
12379 
12380   mng_info->ping_preserve_colormap=MagickFalse;
12381 
12382   value=GetImageOption(image_info,"png:preserve-colormap");
12383   if (value == NULL)
12384      value=GetImageArtifact(image,"png:preserve-colormap");
12385   if (value != NULL)
12386      mng_info->ping_preserve_colormap=MagickTrue;
12387 
12388   mng_info->ping_preserve_iCCP=MagickFalse;
12389 
12390   value=GetImageOption(image_info,"png:preserve-iCCP");
12391   if (value == NULL)
12392      value=GetImageArtifact(image,"png:preserve-iCCP");
12393   if (value != NULL)
12394      mng_info->ping_preserve_iCCP=MagickTrue;
12395 
12396   /* These compression-level, compression-strategy, and compression-filter
12397    * defines take precedence over values from the -quality option.
12398    */
12399   value=GetImageOption(image_info,"png:compression-level");
12400   if (value == NULL)
12401      value=GetImageArtifact(image,"png:compression-level");
12402   if (value != NULL)
12403   {
12404       /* We have to add 1 to everything because 0 is a valid input,
12405        * and we want to use 0 (the default) to mean undefined.
12406        */
12407       if (LocaleCompare(value,"0") == 0)
12408         mng_info->write_png_compression_level = 1;
12409 
12410       else if (LocaleCompare(value,"1") == 0)
12411         mng_info->write_png_compression_level = 2;
12412 
12413       else if (LocaleCompare(value,"2") == 0)
12414         mng_info->write_png_compression_level = 3;
12415 
12416       else if (LocaleCompare(value,"3") == 0)
12417         mng_info->write_png_compression_level = 4;
12418 
12419       else if (LocaleCompare(value,"4") == 0)
12420         mng_info->write_png_compression_level = 5;
12421 
12422       else if (LocaleCompare(value,"5") == 0)
12423         mng_info->write_png_compression_level = 6;
12424 
12425       else if (LocaleCompare(value,"6") == 0)
12426         mng_info->write_png_compression_level = 7;
12427 
12428       else if (LocaleCompare(value,"7") == 0)
12429         mng_info->write_png_compression_level = 8;
12430 
12431       else if (LocaleCompare(value,"8") == 0)
12432         mng_info->write_png_compression_level = 9;
12433 
12434       else if (LocaleCompare(value,"9") == 0)
12435         mng_info->write_png_compression_level = 10;
12436 
12437       else
12438         (void) ThrowMagickException(exception,
12439              GetMagickModule(),CoderWarning,
12440              "ignoring invalid defined png:compression-level",
12441              "=%s",value);
12442     }
12443 
12444   value=GetImageOption(image_info,"png:compression-strategy");
12445   if (value == NULL)
12446      value=GetImageArtifact(image,"png:compression-strategy");
12447   if (value != NULL)
12448   {
12449       if (LocaleCompare(value,"0") == 0)
12450         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12451 
12452       else if (LocaleCompare(value,"1") == 0)
12453         mng_info->write_png_compression_strategy = Z_FILTERED+1;
12454 
12455       else if (LocaleCompare(value,"2") == 0)
12456         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12457 
12458       else if (LocaleCompare(value,"3") == 0)
12459 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
12460         mng_info->write_png_compression_strategy = Z_RLE+1;
12461 #else
12462         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12463 #endif
12464 
12465       else if (LocaleCompare(value,"4") == 0)
12466 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
12467         mng_info->write_png_compression_strategy = Z_FIXED+1;
12468 #else
12469         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12470 #endif
12471 
12472       else
12473         (void) ThrowMagickException(exception,
12474              GetMagickModule(),CoderWarning,
12475              "ignoring invalid defined png:compression-strategy",
12476              "=%s",value);
12477     }
12478 
12479   value=GetImageOption(image_info,"png:compression-filter");
12480   if (value == NULL)
12481      value=GetImageArtifact(image,"png:compression-filter");
12482   if (value != NULL)
12483   {
12484       /* To do: combinations of filters allowed by libpng
12485        * masks 0x08 through 0xf8
12486        *
12487        * Implement this as a comma-separated list of 0,1,2,3,4,5
12488        * where 5 is a special case meaning PNG_ALL_FILTERS.
12489        */
12490 
12491       if (LocaleCompare(value,"0") == 0)
12492         mng_info->write_png_compression_filter = 1;
12493 
12494       else if (LocaleCompare(value,"1") == 0)
12495         mng_info->write_png_compression_filter = 2;
12496 
12497       else if (LocaleCompare(value,"2") == 0)
12498         mng_info->write_png_compression_filter = 3;
12499 
12500       else if (LocaleCompare(value,"3") == 0)
12501         mng_info->write_png_compression_filter = 4;
12502 
12503       else if (LocaleCompare(value,"4") == 0)
12504         mng_info->write_png_compression_filter = 5;
12505 
12506       else if (LocaleCompare(value,"5") == 0)
12507         mng_info->write_png_compression_filter = 6;
12508 
12509       else
12510         (void) ThrowMagickException(exception,
12511              GetMagickModule(),CoderWarning,
12512              "ignoring invalid defined png:compression-filter",
12513              "=%s",value);
12514   }
12515 
12516   for (source=0; source<8; source++)
12517   {
12518     value = NULL;
12519 
12520     if (source == 0)
12521       value=GetImageOption(image_info,"png:exclude-chunks");
12522 
12523     if (source == 1)
12524       value=GetImageArtifact(image,"png:exclude-chunks");
12525 
12526     if (source == 2)
12527       value=GetImageOption(image_info,"png:exclude-chunk");
12528 
12529     if (source == 3)
12530       value=GetImageArtifact(image,"png:exclude-chunk");
12531 
12532     if (source == 4)
12533       value=GetImageOption(image_info,"png:include-chunks");
12534 
12535     if (source == 5)
12536       value=GetImageArtifact(image,"png:include-chunks");
12537 
12538     if (source == 6)
12539       value=GetImageOption(image_info,"png:include-chunk");
12540 
12541     if (source == 7)
12542       value=GetImageArtifact(image,"png:include-chunk");
12543 
12544     if (value == NULL)
12545        continue;
12546 
12547     if (source < 4)
12548       excluding = MagickTrue;
12549     else
12550       excluding = MagickFalse;
12551 
12552     if (logging != MagickFalse)
12553       {
12554         if (source == 0 || source == 2)
12555            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12556               "  png:exclude-chunk=%s found in image options.\n", value);
12557         else if (source == 1 || source == 3)
12558            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12559               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12560         else if (source == 4 || source == 6)
12561            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562               "  png:include-chunk=%s found in image options.\n", value);
12563         else /* if (source == 5 || source == 7) */
12564            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12565               "  png:include-chunk=%s found in image artifacts.\n", value);
12566       }
12567 
12568     if (IsOptionMember("all",value) != MagickFalse)
12569       {
12570         mng_info->ping_exclude_bKGD=excluding;
12571         mng_info->ping_exclude_caNv=excluding;
12572         mng_info->ping_exclude_cHRM=excluding;
12573         mng_info->ping_exclude_date=excluding;
12574         mng_info->ping_exclude_EXIF=excluding;
12575         mng_info->ping_exclude_eXIf=excluding;
12576         mng_info->ping_exclude_gAMA=excluding;
12577         mng_info->ping_exclude_iCCP=excluding;
12578         /* mng_info->ping_exclude_iTXt=excluding; */
12579         mng_info->ping_exclude_oFFs=excluding;
12580         mng_info->ping_exclude_pHYs=excluding;
12581         mng_info->ping_exclude_sRGB=excluding;
12582         mng_info->ping_exclude_tEXt=excluding;
12583         mng_info->ping_exclude_tIME=excluding;
12584         mng_info->ping_exclude_tRNS=excluding;
12585         mng_info->ping_exclude_zCCP=excluding;
12586         mng_info->ping_exclude_zTXt=excluding;
12587       }
12588 
12589     if (IsOptionMember("none",value) != MagickFalse)
12590       {
12591         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12592           MagickTrue;
12593         mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12594           MagickTrue;
12595         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12596           MagickTrue;
12597         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12598           MagickTrue;
12599         mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12600           MagickTrue;
12601         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12602           MagickTrue;
12603         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12604           MagickTrue;
12605         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12606           MagickTrue;
12607         /* mng_info->ping_exclude_iTXt=!excluding; */
12608         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12609           MagickTrue;
12610         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12611           MagickTrue;
12612         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12613           MagickTrue;
12614         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12615           MagickTrue;
12616         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12617           MagickTrue;
12618         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12619           MagickTrue;
12620         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12621           MagickTrue;
12622         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12623           MagickTrue;
12624       }
12625 
12626     if (IsOptionMember("bkgd",value) != MagickFalse)
12627       mng_info->ping_exclude_bKGD=excluding;
12628 
12629     if (IsOptionMember("caNv",value) != MagickFalse)
12630       mng_info->ping_exclude_caNv=excluding;
12631 
12632     if (IsOptionMember("chrm",value) != MagickFalse)
12633       mng_info->ping_exclude_cHRM=excluding;
12634 
12635     if (IsOptionMember("date",value) != MagickFalse)
12636       mng_info->ping_exclude_date=excluding;
12637 
12638     if (IsOptionMember("exif",value) != MagickFalse)
12639       {
12640         mng_info->ping_exclude_EXIF=excluding;
12641         mng_info->ping_exclude_eXIf=excluding;
12642       }
12643 
12644     if (IsOptionMember("gama",value) != MagickFalse)
12645       mng_info->ping_exclude_gAMA=excluding;
12646 
12647     if (IsOptionMember("iccp",value) != MagickFalse)
12648       mng_info->ping_exclude_iCCP=excluding;
12649 
12650 #if 0
12651     if (IsOptionMember("itxt",value) != MagickFalse)
12652       mng_info->ping_exclude_iTXt=excluding;
12653 #endif
12654 
12655     if (IsOptionMember("offs",value) != MagickFalse)
12656       mng_info->ping_exclude_oFFs=excluding;
12657 
12658     if (IsOptionMember("phys",value) != MagickFalse)
12659       mng_info->ping_exclude_pHYs=excluding;
12660 
12661     if (IsOptionMember("srgb",value) != MagickFalse)
12662       mng_info->ping_exclude_sRGB=excluding;
12663 
12664     if (IsOptionMember("text",value) != MagickFalse)
12665       mng_info->ping_exclude_tEXt=excluding;
12666 
12667     if (IsOptionMember("time",value) != MagickFalse)
12668       mng_info->ping_exclude_tIME=excluding;
12669 
12670     if (IsOptionMember("trns",value) != MagickFalse)
12671       mng_info->ping_exclude_tRNS=excluding;
12672 
12673     if (IsOptionMember("zccp",value) != MagickFalse)
12674       mng_info->ping_exclude_zCCP=excluding;
12675 
12676     if (IsOptionMember("ztxt",value) != MagickFalse)
12677       mng_info->ping_exclude_zTXt=excluding;
12678   }
12679 
12680   if (logging != MagickFalse)
12681   {
12682     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12683       "  Chunks to be excluded from the output png:");
12684     if (mng_info->ping_exclude_bKGD != MagickFalse)
12685       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686           "    bKGD");
12687     if (mng_info->ping_exclude_caNv != MagickFalse)
12688       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12689           "    caNv");
12690     if (mng_info->ping_exclude_cHRM != MagickFalse)
12691       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12692           "    cHRM");
12693     if (mng_info->ping_exclude_date != MagickFalse)
12694       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12695           "    date");
12696     if (mng_info->ping_exclude_EXIF != MagickFalse)
12697       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12698           "    EXIF");
12699     if (mng_info->ping_exclude_eXIf != MagickFalse)
12700       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12701           "    eXIf");
12702     if (mng_info->ping_exclude_gAMA != MagickFalse)
12703       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12704           "    gAMA");
12705     if (mng_info->ping_exclude_iCCP != MagickFalse)
12706       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12707           "    iCCP");
12708 #if 0
12709     if (mng_info->ping_exclude_iTXt != MagickFalse)
12710       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12711           "    iTXt");
12712 #endif
12713 
12714     if (mng_info->ping_exclude_oFFs != MagickFalse)
12715       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716           "    oFFs");
12717     if (mng_info->ping_exclude_pHYs != MagickFalse)
12718       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12719           "    pHYs");
12720     if (mng_info->ping_exclude_sRGB != MagickFalse)
12721       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12722           "    sRGB");
12723     if (mng_info->ping_exclude_tEXt != MagickFalse)
12724       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12725           "    tEXt");
12726     if (mng_info->ping_exclude_tIME != MagickFalse)
12727       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12728           "    tIME");
12729     if (mng_info->ping_exclude_tRNS != MagickFalse)
12730       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12731           "    tRNS");
12732     if (mng_info->ping_exclude_zCCP != MagickFalse)
12733       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12734           "    zCCP");
12735     if (mng_info->ping_exclude_zTXt != MagickFalse)
12736       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12737           "    zTXt");
12738   }
12739 
12740   mng_info->need_blob = MagickTrue;
12741 
12742   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12743 
12744   mng_info=MngInfoFreeStruct(mng_info);
12745 
12746   if (logging != MagickFalse)
12747     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12748 
12749   return(status);
12750 }
12751 
12752 #if defined(JNG_SUPPORTED)
12753 
12754 /* Write one JNG image */
WriteOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12755 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12756    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12757 {
12758   Image
12759     *jpeg_image;
12760 
12761   ImageInfo
12762     *jpeg_image_info;
12763 
12764   MagickBooleanType
12765     logging,
12766     status;
12767 
12768   size_t
12769     length;
12770 
12771   unsigned char
12772     *blob,
12773     chunk[80],
12774     *p;
12775 
12776   unsigned int
12777     jng_alpha_compression_method,
12778     jng_alpha_sample_depth,
12779     jng_color_type,
12780     transparent;
12781 
12782   size_t
12783     jng_alpha_quality,
12784     jng_quality;
12785 
12786   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12787     "  Enter WriteOneJNGImage()");
12788 
12789   blob=(unsigned char *) NULL;
12790   jpeg_image=(Image *) NULL;
12791   jpeg_image_info=(ImageInfo *) NULL;
12792   length=0;
12793 
12794   status=MagickTrue;
12795   transparent=image_info->type==GrayscaleAlphaType ||
12796      image_info->type==TrueColorAlphaType ||
12797      image->alpha_trait != UndefinedPixelTrait;
12798 
12799   jng_alpha_sample_depth = 0;
12800 
12801   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12802 
12803   jng_alpha_compression_method=(image->compression==JPEGCompression ||
12804     image_info->compression==JPEGCompression) ? 8 : 0;
12805 
12806   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12807       image_info->quality;
12808 
12809   if (jng_alpha_quality >= 1000)
12810     jng_alpha_quality /= 1000;
12811 
12812   length=0;
12813 
12814   if (transparent != 0)
12815     {
12816       jng_color_type=14;
12817 
12818       /* Create JPEG blob, image, and image_info */
12819       if (logging != MagickFalse)
12820         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12821           "  Creating jpeg_image_info for alpha.");
12822 
12823       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12824 
12825       if (jpeg_image_info == (ImageInfo *) NULL)
12826         {
12827           jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12828           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12829         }
12830 
12831       if (logging != MagickFalse)
12832         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12833           "  Creating jpeg_image.");
12834 
12835       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12836       if (jpeg_image == (Image *) NULL)
12837         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12838       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12839       jpeg_image->alpha_trait=UndefinedPixelTrait;
12840       jpeg_image->quality=jng_alpha_quality;
12841       jpeg_image_info->type=GrayscaleType;
12842       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12843       (void) AcquireUniqueFilename(jpeg_image->filename);
12844       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12845         "%s",jpeg_image->filename);
12846     }
12847   else
12848     {
12849       jng_alpha_compression_method=0;
12850       jng_color_type=10;
12851       jng_alpha_sample_depth=0;
12852     }
12853 
12854   /* To do: check bit depth of PNG alpha channel */
12855 
12856   /* Check if image is grayscale. */
12857   if (image_info->type != TrueColorAlphaType && image_info->type !=
12858     TrueColorType && SetImageGray(image,exception))
12859     jng_color_type-=2;
12860 
12861   if (logging != MagickFalse)
12862     {
12863         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12864           "    JNG Quality           = %d",(int) jng_quality);
12865         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12866           "    JNG Color Type        = %d",jng_color_type);
12867         if (transparent != 0)
12868           {
12869             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12870               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12871             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12872               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12873             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12874               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12875           }
12876     }
12877 
12878   if (transparent != 0)
12879     {
12880       if (jng_alpha_compression_method==0)
12881         {
12882           const char
12883             *value;
12884 
12885           /* Encode alpha as a grayscale PNG blob */
12886           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12887             exception);
12888           if (status == MagickFalse)
12889             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12890 
12891           if (logging != MagickFalse)
12892             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12893               "  Creating PNG blob.");
12894 
12895           (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12896              MagickPathExtent);
12897           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12898           jpeg_image_info->interlace=NoInterlace;
12899 
12900           /* Exclude all ancillary chunks */
12901           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12902 
12903           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12904             &length,exception);
12905 
12906           /* Retrieve sample depth used */
12907           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12908           if (value != (char *) NULL)
12909             jng_alpha_sample_depth= (unsigned int) value[0];
12910         }
12911       else
12912         {
12913           /* Encode alpha as a grayscale JPEG blob */
12914 
12915           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12916             exception);
12917           if (status == MagickFalse)
12918             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12919 
12920           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12921             MagickPathExtent);
12922           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12923           jpeg_image_info->interlace=NoInterlace;
12924           if (logging != MagickFalse)
12925             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12926               "  Creating blob.");
12927           blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12928              jpeg_image,&length,
12929            exception);
12930           jng_alpha_sample_depth=8;
12931 
12932           if (logging != MagickFalse)
12933             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12934               "  Successfully read jpeg_image into a blob, length=%.20g.",
12935               (double) length);
12936 
12937         }
12938       /* Destroy JPEG image and image_info */
12939       jpeg_image=DestroyImage(jpeg_image);
12940       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12941       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12942     }
12943 
12944   /* Write JHDR chunk */
12945   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12946   PNGType(chunk,mng_JHDR);
12947   LogPNGChunk(logging,mng_JHDR,16L);
12948   PNGLong(chunk+4,(png_uint_32) image->columns);
12949   PNGLong(chunk+8,(png_uint_32) image->rows);
12950   chunk[12]=jng_color_type;
12951   chunk[13]=8;  /* sample depth */
12952   chunk[14]=8; /*jng_image_compression_method */
12953   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12954   chunk[16]=jng_alpha_sample_depth;
12955   chunk[17]=jng_alpha_compression_method;
12956   chunk[18]=0; /*jng_alpha_filter_method */
12957   chunk[19]=0; /*jng_alpha_interlace_method */
12958   (void) WriteBlob(image,20,chunk);
12959   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12960   if (logging != MagickFalse)
12961     {
12962       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12963         "    JNG width:%15lu",(unsigned long) image->columns);
12964 
12965       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12966         "    JNG height:%14lu",(unsigned long) image->rows);
12967 
12968       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969         "    JNG color type:%10d",jng_color_type);
12970 
12971       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12972         "    JNG sample depth:%8d",8);
12973 
12974       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12975         "    JNG compression:%9d",8);
12976 
12977       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12978         "    JNG interlace:%11d",0);
12979 
12980       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12981         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12982 
12983       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12985 
12986       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12987         "    JNG alpha filter:%8d",0);
12988 
12989       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12990         "    JNG alpha interlace:%5d",0);
12991     }
12992 
12993   /*
12994      Write leading ancillary chunks
12995   */
12996 
12997   if (transparent != 0)
12998   {
12999     /*
13000       Write JNG bKGD chunk
13001     */
13002 
13003     unsigned char
13004       blue,
13005       green,
13006       red;
13007 
13008     ssize_t
13009       num_bytes;
13010 
13011     if (jng_color_type == 8 || jng_color_type == 12)
13012       num_bytes=6L;
13013     else
13014       num_bytes=10L;
13015     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
13016     PNGType(chunk,mng_bKGD);
13017     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
13018     red=ScaleQuantumToChar(image->background_color.red);
13019     green=ScaleQuantumToChar(image->background_color.green);
13020     blue=ScaleQuantumToChar(image->background_color.blue);
13021     *(chunk+4)=0;
13022     *(chunk+5)=red;
13023     *(chunk+6)=0;
13024     *(chunk+7)=green;
13025     *(chunk+8)=0;
13026     *(chunk+9)=blue;
13027     (void) WriteBlob(image,(size_t) num_bytes,chunk);
13028     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13029   }
13030 
13031   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13032     {
13033       /*
13034         Write JNG sRGB chunk
13035       */
13036       (void) WriteBlobMSBULong(image,1L);
13037       PNGType(chunk,mng_sRGB);
13038       LogPNGChunk(logging,mng_sRGB,1L);
13039 
13040       if (image->rendering_intent != UndefinedIntent)
13041         chunk[4]=(unsigned char)
13042           Magick_RenderingIntent_to_PNG_RenderingIntent(
13043           (image->rendering_intent));
13044 
13045       else
13046         chunk[4]=(unsigned char)
13047           Magick_RenderingIntent_to_PNG_RenderingIntent(
13048           (PerceptualIntent));
13049 
13050       (void) WriteBlob(image,5,chunk);
13051       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13052     }
13053   else
13054     {
13055       if (image->gamma != 0.0)
13056         {
13057           /*
13058              Write JNG gAMA chunk
13059           */
13060           (void) WriteBlobMSBULong(image,4L);
13061           PNGType(chunk,mng_gAMA);
13062           LogPNGChunk(logging,mng_gAMA,4L);
13063           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13064           (void) WriteBlob(image,8,chunk);
13065           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13066         }
13067 
13068       if ((mng_info->equal_chrms == MagickFalse) &&
13069           (image->chromaticity.red_primary.x != 0.0))
13070         {
13071           PrimaryInfo
13072             primary;
13073 
13074           /*
13075              Write JNG cHRM chunk
13076           */
13077           (void) WriteBlobMSBULong(image,32L);
13078           PNGType(chunk,mng_cHRM);
13079           LogPNGChunk(logging,mng_cHRM,32L);
13080           primary=image->chromaticity.white_point;
13081           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13082           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13083           primary=image->chromaticity.red_primary;
13084           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13085           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13086           primary=image->chromaticity.green_primary;
13087           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13088           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13089           primary=image->chromaticity.blue_primary;
13090           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13091           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13092           (void) WriteBlob(image,36,chunk);
13093           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13094         }
13095     }
13096 
13097   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13098     {
13099       /*
13100          Write JNG pHYs chunk
13101       */
13102       (void) WriteBlobMSBULong(image,9L);
13103       PNGType(chunk,mng_pHYs);
13104       LogPNGChunk(logging,mng_pHYs,9L);
13105       if (image->units == PixelsPerInchResolution)
13106         {
13107           PNGLong(chunk+4,(png_uint_32)
13108             (image->resolution.x*100.0/2.54+0.5));
13109 
13110           PNGLong(chunk+8,(png_uint_32)
13111             (image->resolution.y*100.0/2.54+0.5));
13112 
13113           chunk[12]=1;
13114         }
13115 
13116       else
13117         {
13118           if (image->units == PixelsPerCentimeterResolution)
13119             {
13120               PNGLong(chunk+4,(png_uint_32)
13121                 (image->resolution.x*100.0+0.5));
13122 
13123               PNGLong(chunk+8,(png_uint_32)
13124                 (image->resolution.y*100.0+0.5));
13125 
13126               chunk[12]=1;
13127             }
13128 
13129           else
13130             {
13131               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13132               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13133               chunk[12]=0;
13134             }
13135         }
13136       (void) WriteBlob(image,13,chunk);
13137       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13138     }
13139 
13140   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13141     {
13142       /*
13143          Write JNG oFFs chunk
13144       */
13145       (void) WriteBlobMSBULong(image,9L);
13146       PNGType(chunk,mng_oFFs);
13147       LogPNGChunk(logging,mng_oFFs,9L);
13148       PNGsLong(chunk+4,(ssize_t) (image->page.x));
13149       PNGsLong(chunk+8,(ssize_t) (image->page.y));
13150       chunk[12]=0;
13151       (void) WriteBlob(image,13,chunk);
13152       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13153     }
13154 
13155   if (transparent != 0)
13156     {
13157       if (jng_alpha_compression_method==0)
13158         {
13159           register ssize_t
13160             i;
13161 
13162           size_t
13163             len;
13164 
13165           /* Write IDAT chunk header */
13166           if (logging != MagickFalse)
13167             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13168               "  Write IDAT chunks from blob, length=%.20g.",(double)
13169               length);
13170 
13171           /* Copy IDAT chunks */
13172           len=0;
13173           p=blob+8;
13174           for (i=8; i<(ssize_t) length; i+=len+12)
13175           {
13176             len=(((unsigned int) *(p    ) & 0xff) << 24) +
13177                 (((unsigned int) *(p + 1) & 0xff) << 16) +
13178                 (((unsigned int) *(p + 2) & 0xff) <<  8) +
13179                 (((unsigned int) *(p + 3) & 0xff)      ) ;
13180             p+=4;
13181 
13182             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13183               {
13184                 /* Found an IDAT chunk. */
13185                 (void) WriteBlobMSBULong(image,len);
13186                 LogPNGChunk(logging,mng_IDAT,len);
13187                 (void) WriteBlob(image,len+4,p);
13188                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13189               }
13190 
13191             else
13192               {
13193                 if (logging != MagickFalse)
13194                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13195                     "    Skipping %c%c%c%c chunk, length=%.20g.",
13196                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
13197               }
13198             p+=(8+len);
13199           }
13200         }
13201       else if (length != 0)
13202         {
13203           /* Write JDAA chunk header */
13204           if (logging != MagickFalse)
13205             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13206               "  Write JDAA chunk, length=%.20g.",(double) length);
13207           (void) WriteBlobMSBULong(image,(size_t) length);
13208           PNGType(chunk,mng_JDAA);
13209           LogPNGChunk(logging,mng_JDAA,length);
13210           /* Write JDAT chunk(s) data */
13211           (void) WriteBlob(image,4,chunk);
13212           (void) WriteBlob(image,length,blob);
13213           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13214              (uInt) length));
13215         }
13216       blob=(unsigned char *) RelinquishMagickMemory(blob);
13217     }
13218 
13219   /* Encode image as a JPEG blob */
13220   if (logging != MagickFalse)
13221     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13222       "  Creating jpeg_image_info.");
13223   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13224   if (jpeg_image_info == (ImageInfo *) NULL)
13225     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13226 
13227   if (logging != MagickFalse)
13228     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13229       "  Creating jpeg_image.");
13230 
13231   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13232   if (jpeg_image == (Image *) NULL)
13233     {
13234       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13235       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13236     }
13237   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13238 
13239   (void) AcquireUniqueFilename(jpeg_image->filename);
13240   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13241     jpeg_image->filename);
13242 
13243   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13244     exception);
13245 
13246   if (logging != MagickFalse)
13247     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13248       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13249       (double) jpeg_image->rows);
13250 
13251   if (status == MagickFalse)
13252     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13253 
13254   if (jng_color_type == 8 || jng_color_type == 12)
13255     jpeg_image_info->type=GrayscaleType;
13256 
13257   jpeg_image_info->quality=jng_quality;
13258   jpeg_image->quality=jng_quality;
13259   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13260   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13261 
13262   if (logging != MagickFalse)
13263     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13264       "  Creating blob.");
13265 
13266   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13267     exception);
13268 
13269   if (logging != MagickFalse)
13270     {
13271       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13272         "  Successfully read jpeg_image into a blob, length=%.20g.",
13273         (double) length);
13274 
13275       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13276         "  Write JDAT chunk, length=%.20g.",(double) length);
13277     }
13278 
13279   /* Write JDAT chunk(s) */
13280   (void) WriteBlobMSBULong(image,(size_t) length);
13281   PNGType(chunk,mng_JDAT);
13282   LogPNGChunk(logging,mng_JDAT,length);
13283   (void) WriteBlob(image,4,chunk);
13284   (void) WriteBlob(image,length,blob);
13285   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13286 
13287   jpeg_image=DestroyImage(jpeg_image);
13288   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13289   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13290   blob=(unsigned char *) RelinquishMagickMemory(blob);
13291 
13292   /* Write IEND chunk */
13293   (void) WriteBlobMSBULong(image,0L);
13294   PNGType(chunk,mng_IEND);
13295   LogPNGChunk(logging,mng_IEND,0);
13296   (void) WriteBlob(image,4,chunk);
13297   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13298 
13299   if (logging != MagickFalse)
13300     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13301       "  exit WriteOneJNGImage()");
13302 
13303   return(status);
13304 }
13305 
13306 /*
13307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13308 %                                                                             %
13309 %                                                                             %
13310 %                                                                             %
13311 %   W r i t e J N G I m a g e                                                 %
13312 %                                                                             %
13313 %                                                                             %
13314 %                                                                             %
13315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13316 %
13317 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13318 %
13319 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
13320 %
13321 %  The format of the WriteJNGImage method is:
13322 %
13323 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13324 %        Image *image,ExceptionInfo *exception)
13325 %
13326 %  A description of each parameter follows:
13327 %
13328 %    o image_info: the image info.
13329 %
13330 %    o image:  The image.
13331 %
13332 %    o exception: return any errors or warnings in this structure.
13333 %
13334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13335 */
WriteJNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)13336 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13337   Image *image, ExceptionInfo *exception)
13338 {
13339   MagickBooleanType
13340     logging,
13341     status;
13342 
13343   MngInfo
13344     *mng_info;
13345 
13346   /*
13347     Open image file.
13348   */
13349   assert(image_info != (const ImageInfo *) NULL);
13350   assert(image_info->signature == MagickCoreSignature);
13351   assert(image != (Image *) NULL);
13352   assert(image->signature == MagickCoreSignature);
13353   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13354   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13355   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13356   if (status == MagickFalse)
13357     return(status);
13358   if ((image->columns > 65535UL) || (image->rows > 65535UL))
13359     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13360 
13361   /*
13362     Allocate a MngInfo structure.
13363   */
13364   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13365   if (mng_info == (MngInfo *) NULL)
13366     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13367   /*
13368     Initialize members of the MngInfo structure.
13369   */
13370   (void) memset(mng_info,0,sizeof(MngInfo));
13371   mng_info->image=image;
13372 
13373   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13374 
13375   status=WriteOneJNGImage(mng_info,image_info,image,exception);
13376   mng_info=MngInfoFreeStruct(mng_info);
13377   (void) CloseBlob(image);
13378 
13379   (void) CatchImageException(image);
13380   if (logging != MagickFalse)
13381     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13382   return(status);
13383 }
13384 #endif
13385 
WriteMNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)13386 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13387   Image *image, ExceptionInfo *exception)
13388 {
13389   Image
13390     *next_image;
13391 
13392   MagickBooleanType
13393     status;
13394 
13395   volatile MagickBooleanType
13396     logging;
13397 
13398   MngInfo
13399     *mng_info;
13400 
13401   int
13402     image_count,
13403     need_iterations,
13404     need_matte;
13405 
13406   volatile int
13407 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13408     defined(PNG_MNG_FEATURES_SUPPORTED)
13409     need_local_plte,
13410 #endif
13411     all_images_are_gray,
13412     need_defi,
13413     use_global_plte;
13414 
13415   register ssize_t
13416     i;
13417 
13418   unsigned char
13419     chunk[800];
13420 
13421   volatile unsigned int
13422     write_jng,
13423     write_mng;
13424 
13425   volatile size_t
13426     scene;
13427 
13428   size_t
13429     final_delay=0,
13430     imageListLength,
13431     initial_delay;
13432 
13433 #if (PNG_LIBPNG_VER < 10200)
13434     if (image_info->verbose)
13435       printf("Your PNG library (libpng-%s) is rather old.\n",
13436          PNG_LIBPNG_VER_STRING);
13437 #endif
13438 
13439   /*
13440     Open image file.
13441   */
13442   assert(image_info != (const ImageInfo *) NULL);
13443   assert(image_info->signature == MagickCoreSignature);
13444   assert(image != (Image *) NULL);
13445   assert(image->signature == MagickCoreSignature);
13446   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13447   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13448   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13449   if (status == MagickFalse)
13450     return(status);
13451 
13452   /*
13453     Allocate a MngInfo structure.
13454   */
13455   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13456   if (mng_info == (MngInfo *) NULL)
13457     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13458   /*
13459     Initialize members of the MngInfo structure.
13460   */
13461   (void) memset(mng_info,0,sizeof(MngInfo));
13462   mng_info->image=image;
13463   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13464 
13465   /*
13466    * See if user has requested a specific PNG subformat to be used
13467    * for all of the PNGs in the MNG being written, e.g.,
13468    *
13469    *    convert *.png png8:animation.mng
13470    *
13471    * To do: check -define png:bit_depth and png:color_type as well,
13472    * or perhaps use mng:bit_depth and mng:color_type instead for
13473    * global settings.
13474    */
13475 
13476   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13477   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13478   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13479 
13480   write_jng=MagickFalse;
13481   if (image_info->compression == JPEGCompression)
13482     write_jng=MagickTrue;
13483 
13484   mng_info->adjoin=image_info->adjoin &&
13485     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13486 
13487   if (logging != MagickFalse)
13488     {
13489       /* Log some info about the input */
13490       Image
13491         *p;
13492 
13493       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13494         "  Checking input image(s)\n"
13495         "    Image_info depth: %.20g,    Type: %d",
13496         (double) image_info->depth, image_info->type);
13497 
13498       scene=0;
13499       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13500       {
13501 
13502         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13503            "    Scene: %.20g\n,   Image depth: %.20g",
13504            (double) scene++, (double) p->depth);
13505 
13506         if (p->alpha_trait != UndefinedPixelTrait)
13507           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13508             "      Matte: True");
13509 
13510         else
13511           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13512             "      Matte: False");
13513 
13514         if (p->storage_class == PseudoClass)
13515           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13516             "      Storage class: PseudoClass");
13517 
13518         else
13519           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13520             "      Storage class: DirectClass");
13521 
13522         if (p->colors)
13523           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13524             "      Number of colors: %.20g",(double) p->colors);
13525 
13526         else
13527           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13528             "      Number of colors: unspecified");
13529 
13530         if (mng_info->adjoin == MagickFalse)
13531           break;
13532       }
13533     }
13534 
13535   use_global_plte=MagickFalse;
13536   all_images_are_gray=MagickFalse;
13537 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13538   need_local_plte=MagickTrue;
13539 #endif
13540   need_defi=MagickFalse;
13541   need_matte=MagickFalse;
13542   mng_info->framing_mode=1;
13543   mng_info->old_framing_mode=1;
13544 
13545   if (write_mng)
13546       if (image_info->page != (char *) NULL)
13547         {
13548           /*
13549             Determine image bounding box.
13550           */
13551           SetGeometry(image,&mng_info->page);
13552           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13553             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13554         }
13555   if (write_mng)
13556     {
13557       unsigned int
13558         need_geom;
13559 
13560       unsigned short
13561         red,
13562         green,
13563         blue;
13564 
13565       const char *
13566         option;
13567 
13568       mng_info->page=image->page;
13569       need_geom=MagickTrue;
13570       if (mng_info->page.width || mng_info->page.height)
13571          need_geom=MagickFalse;
13572       /*
13573         Check all the scenes.
13574       */
13575       initial_delay=image->delay;
13576       need_iterations=MagickFalse;
13577       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13578       mng_info->equal_physs=MagickTrue,
13579       mng_info->equal_gammas=MagickTrue;
13580       mng_info->equal_srgbs=MagickTrue;
13581       mng_info->equal_backgrounds=MagickTrue;
13582       image_count=0;
13583 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13584     defined(PNG_MNG_FEATURES_SUPPORTED)
13585       all_images_are_gray=MagickTrue;
13586       mng_info->equal_palettes=MagickFalse;
13587       need_local_plte=MagickFalse;
13588 #endif
13589       for (next_image=image; next_image != (Image *) NULL; )
13590       {
13591         if (need_geom)
13592           {
13593             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13594               mng_info->page.width=next_image->columns+next_image->page.x;
13595 
13596             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13597               mng_info->page.height=next_image->rows+next_image->page.y;
13598           }
13599 
13600         if (next_image->page.x || next_image->page.y)
13601           need_defi=MagickTrue;
13602 
13603         if (next_image->alpha_trait != UndefinedPixelTrait)
13604           need_matte=MagickTrue;
13605 
13606         if ((int) next_image->dispose >= BackgroundDispose)
13607           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13608                next_image->page.x || next_image->page.y ||
13609               ((next_image->columns < mng_info->page.width) &&
13610                (next_image->rows < mng_info->page.height)))
13611             mng_info->need_fram=MagickTrue;
13612 
13613         if (next_image->iterations)
13614           need_iterations=MagickTrue;
13615 
13616         final_delay=next_image->delay;
13617 
13618         if (final_delay != initial_delay || final_delay > 1UL*
13619            next_image->ticks_per_second)
13620           mng_info->need_fram=1;
13621 
13622 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13623     defined(PNG_MNG_FEATURES_SUPPORTED)
13624         /*
13625           check for global palette possibility.
13626         */
13627         if (image->alpha_trait != UndefinedPixelTrait)
13628            need_local_plte=MagickTrue;
13629 
13630         if (need_local_plte == 0)
13631           {
13632             if (SetImageGray(image,exception) == MagickFalse)
13633               all_images_are_gray=MagickFalse;
13634             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13635             if (use_global_plte == 0)
13636               use_global_plte=mng_info->equal_palettes;
13637             need_local_plte=!mng_info->equal_palettes;
13638           }
13639 #endif
13640         if (GetNextImageInList(next_image) != (Image *) NULL)
13641           {
13642             if (next_image->background_color.red !=
13643                 next_image->next->background_color.red ||
13644                 next_image->background_color.green !=
13645                 next_image->next->background_color.green ||
13646                 next_image->background_color.blue !=
13647                 next_image->next->background_color.blue)
13648               mng_info->equal_backgrounds=MagickFalse;
13649 
13650             if (next_image->gamma != next_image->next->gamma)
13651               mng_info->equal_gammas=MagickFalse;
13652 
13653             if (next_image->rendering_intent !=
13654                 next_image->next->rendering_intent)
13655               mng_info->equal_srgbs=MagickFalse;
13656 
13657             if ((next_image->units != next_image->next->units) ||
13658                 (next_image->resolution.x != next_image->next->resolution.x) ||
13659                 (next_image->resolution.y != next_image->next->resolution.y))
13660               mng_info->equal_physs=MagickFalse;
13661 
13662             if (mng_info->equal_chrms)
13663               {
13664                 if (next_image->chromaticity.red_primary.x !=
13665                     next_image->next->chromaticity.red_primary.x ||
13666                     next_image->chromaticity.red_primary.y !=
13667                     next_image->next->chromaticity.red_primary.y ||
13668                     next_image->chromaticity.green_primary.x !=
13669                     next_image->next->chromaticity.green_primary.x ||
13670                     next_image->chromaticity.green_primary.y !=
13671                     next_image->next->chromaticity.green_primary.y ||
13672                     next_image->chromaticity.blue_primary.x !=
13673                     next_image->next->chromaticity.blue_primary.x ||
13674                     next_image->chromaticity.blue_primary.y !=
13675                     next_image->next->chromaticity.blue_primary.y ||
13676                     next_image->chromaticity.white_point.x !=
13677                     next_image->next->chromaticity.white_point.x ||
13678                     next_image->chromaticity.white_point.y !=
13679                     next_image->next->chromaticity.white_point.y)
13680                   mng_info->equal_chrms=MagickFalse;
13681               }
13682           }
13683         image_count++;
13684         next_image=GetNextImageInList(next_image);
13685       }
13686       if (image_count < 2)
13687         {
13688           mng_info->equal_backgrounds=MagickFalse;
13689           mng_info->equal_chrms=MagickFalse;
13690           mng_info->equal_gammas=MagickFalse;
13691           mng_info->equal_srgbs=MagickFalse;
13692           mng_info->equal_physs=MagickFalse;
13693           use_global_plte=MagickFalse;
13694 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13695           need_local_plte=MagickTrue;
13696 #endif
13697           need_iterations=MagickFalse;
13698         }
13699 
13700      if (mng_info->need_fram == MagickFalse)
13701        {
13702          /*
13703            Only certain framing rates 100/n are exactly representable without
13704            the FRAM chunk but we'll allow some slop in VLC files
13705          */
13706          if (final_delay == 0)
13707            {
13708              if (need_iterations != MagickFalse)
13709                {
13710                  /*
13711                    It's probably a GIF with loop; don't run it *too* fast.
13712                  */
13713                  if (mng_info->adjoin)
13714                    {
13715                      final_delay=10;
13716                      (void) ThrowMagickException(exception,GetMagickModule(),
13717                        CoderWarning,
13718                        "input has zero delay between all frames; assuming",
13719                        " 10 cs `%s'","");
13720                    }
13721                }
13722              else
13723                mng_info->ticks_per_second=0;
13724            }
13725          if (final_delay != 0)
13726            mng_info->ticks_per_second=(png_uint_32)
13727               (image->ticks_per_second/final_delay);
13728          if (final_delay > 50)
13729            mng_info->ticks_per_second=2;
13730 
13731          if (final_delay > 75)
13732            mng_info->ticks_per_second=1;
13733 
13734          if (final_delay > 125)
13735            mng_info->need_fram=MagickTrue;
13736 
13737          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13738             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13739             (final_delay != 25) && (final_delay != 50) &&
13740             (final_delay != (size_t) image->ticks_per_second))
13741            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13742        }
13743 
13744      if (mng_info->need_fram != MagickFalse)
13745         mng_info->ticks_per_second=image->ticks_per_second;
13746      /*
13747         If pseudocolor, we should also check to see if all the
13748         palettes are identical and write a global PLTE if they are.
13749         ../glennrp Feb 99.
13750      */
13751      /*
13752         Write the MNG version 1.0 signature and MHDR chunk.
13753      */
13754      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13755      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13756      PNGType(chunk,mng_MHDR);
13757      LogPNGChunk(logging,mng_MHDR,28L);
13758      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13759      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13760      PNGLong(chunk+12,mng_info->ticks_per_second);
13761      PNGLong(chunk+16,0L);  /* layer count=unknown */
13762      PNGLong(chunk+20,0L);  /* frame count=unknown */
13763      PNGLong(chunk+24,0L);  /* play time=unknown   */
13764      if (write_jng)
13765        {
13766          if (need_matte)
13767            {
13768              if (need_defi || mng_info->need_fram || use_global_plte)
13769                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13770 
13771              else
13772                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13773            }
13774 
13775          else
13776            {
13777              if (need_defi || mng_info->need_fram || use_global_plte)
13778                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13779 
13780              else
13781                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13782            }
13783        }
13784 
13785      else
13786        {
13787          if (need_matte)
13788            {
13789              if (need_defi || mng_info->need_fram || use_global_plte)
13790                PNGLong(chunk+28,11L);    /* simplicity=LC */
13791 
13792              else
13793                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13794            }
13795 
13796          else
13797            {
13798              if (need_defi || mng_info->need_fram || use_global_plte)
13799                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13800 
13801              else
13802                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13803            }
13804        }
13805      (void) WriteBlob(image,32,chunk);
13806      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13807      option=GetImageOption(image_info,"mng:need-cacheoff");
13808      if (option != (const char *) NULL)
13809        {
13810          size_t
13811            length;
13812          /*
13813            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13814          */
13815          PNGType(chunk,mng_nEED);
13816          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13817          (void) WriteBlobMSBULong(image,(size_t) length);
13818          LogPNGChunk(logging,mng_nEED,(size_t) length);
13819          length+=4;
13820          (void) WriteBlob(image,length,chunk);
13821          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13822        }
13823      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13824          (GetNextImageInList(image) != (Image *) NULL) &&
13825          (image->iterations != 1))
13826        {
13827          /*
13828            Write MNG TERM chunk
13829          */
13830          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13831          PNGType(chunk,mng_TERM);
13832          LogPNGChunk(logging,mng_TERM,10L);
13833          chunk[4]=3;  /* repeat animation */
13834          chunk[5]=0;  /* show last frame when done */
13835          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13836             final_delay/MagickMax(image->ticks_per_second,1)));
13837 
13838          if (image->iterations == 0)
13839            PNGLong(chunk+10,PNG_UINT_31_MAX);
13840 
13841          else
13842            PNGLong(chunk+10,(png_uint_32) image->iterations);
13843 
13844          if (logging != MagickFalse)
13845            {
13846              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13847                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13848               final_delay/MagickMax(image->ticks_per_second,1)));
13849 
13850              if (image->iterations == 0)
13851                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13852                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13853 
13854              else
13855                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13856                  "     Image iterations: %.20g",(double) image->iterations);
13857            }
13858          (void) WriteBlob(image,14,chunk);
13859          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13860        }
13861      /*
13862        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13863      */
13864      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13865           mng_info->equal_srgbs)
13866        {
13867          /*
13868            Write MNG sRGB chunk
13869          */
13870          (void) WriteBlobMSBULong(image,1L);
13871          PNGType(chunk,mng_sRGB);
13872          LogPNGChunk(logging,mng_sRGB,1L);
13873 
13874          if (image->rendering_intent != UndefinedIntent)
13875            chunk[4]=(unsigned char)
13876              Magick_RenderingIntent_to_PNG_RenderingIntent(
13877              (image->rendering_intent));
13878 
13879          else
13880            chunk[4]=(unsigned char)
13881              Magick_RenderingIntent_to_PNG_RenderingIntent(
13882                (PerceptualIntent));
13883 
13884          (void) WriteBlob(image,5,chunk);
13885          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13886          mng_info->have_write_global_srgb=MagickTrue;
13887        }
13888 
13889      else
13890        {
13891          if (image->gamma && mng_info->equal_gammas)
13892            {
13893              /*
13894                 Write MNG gAMA chunk
13895              */
13896              (void) WriteBlobMSBULong(image,4L);
13897              PNGType(chunk,mng_gAMA);
13898              LogPNGChunk(logging,mng_gAMA,4L);
13899              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13900              (void) WriteBlob(image,8,chunk);
13901              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13902              mng_info->have_write_global_gama=MagickTrue;
13903            }
13904          if (mng_info->equal_chrms)
13905            {
13906              PrimaryInfo
13907                primary;
13908 
13909              /*
13910                 Write MNG cHRM chunk
13911              */
13912              (void) WriteBlobMSBULong(image,32L);
13913              PNGType(chunk,mng_cHRM);
13914              LogPNGChunk(logging,mng_cHRM,32L);
13915              primary=image->chromaticity.white_point;
13916              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13917              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13918              primary=image->chromaticity.red_primary;
13919              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13920              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13921              primary=image->chromaticity.green_primary;
13922              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13923              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13924              primary=image->chromaticity.blue_primary;
13925              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13926              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13927              (void) WriteBlob(image,36,chunk);
13928              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13929              mng_info->have_write_global_chrm=MagickTrue;
13930            }
13931        }
13932      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13933        {
13934          /*
13935             Write MNG pHYs chunk
13936          */
13937          (void) WriteBlobMSBULong(image,9L);
13938          PNGType(chunk,mng_pHYs);
13939          LogPNGChunk(logging,mng_pHYs,9L);
13940 
13941          if (image->units == PixelsPerInchResolution)
13942            {
13943              PNGLong(chunk+4,(png_uint_32)
13944                (image->resolution.x*100.0/2.54+0.5));
13945 
13946              PNGLong(chunk+8,(png_uint_32)
13947                (image->resolution.y*100.0/2.54+0.5));
13948 
13949              chunk[12]=1;
13950            }
13951 
13952          else
13953            {
13954              if (image->units == PixelsPerCentimeterResolution)
13955                {
13956                  PNGLong(chunk+4,(png_uint_32)
13957                    (image->resolution.x*100.0+0.5));
13958 
13959                  PNGLong(chunk+8,(png_uint_32)
13960                    (image->resolution.y*100.0+0.5));
13961 
13962                  chunk[12]=1;
13963                }
13964 
13965              else
13966                {
13967                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13968                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13969                  chunk[12]=0;
13970                }
13971            }
13972          (void) WriteBlob(image,13,chunk);
13973          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13974        }
13975      /*
13976        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13977        or does not cover the entire frame.
13978      */
13979      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13980          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13981          (image->page.width+image->page.x < mng_info->page.width))
13982          || (image->page.height && (image->page.height+image->page.y
13983          < mng_info->page.height))))
13984        {
13985          (void) WriteBlobMSBULong(image,6L);
13986          PNGType(chunk,mng_BACK);
13987          LogPNGChunk(logging,mng_BACK,6L);
13988          red=ScaleQuantumToShort(image->background_color.red);
13989          green=ScaleQuantumToShort(image->background_color.green);
13990          blue=ScaleQuantumToShort(image->background_color.blue);
13991          PNGShort(chunk+4,red);
13992          PNGShort(chunk+6,green);
13993          PNGShort(chunk+8,blue);
13994          (void) WriteBlob(image,10,chunk);
13995          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13996          if (mng_info->equal_backgrounds)
13997            {
13998              (void) WriteBlobMSBULong(image,6L);
13999              PNGType(chunk,mng_bKGD);
14000              LogPNGChunk(logging,mng_bKGD,6L);
14001              (void) WriteBlob(image,10,chunk);
14002              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14003            }
14004        }
14005 
14006 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
14007      if ((need_local_plte == MagickFalse) &&
14008          (image->storage_class == PseudoClass) &&
14009          (all_images_are_gray == MagickFalse))
14010        {
14011          size_t
14012            data_length;
14013 
14014          /*
14015            Write MNG PLTE chunk
14016          */
14017          data_length=3*image->colors;
14018          (void) WriteBlobMSBULong(image,data_length);
14019          PNGType(chunk,mng_PLTE);
14020          LogPNGChunk(logging,mng_PLTE,data_length);
14021 
14022          for (i=0; i < (ssize_t) image->colors; i++)
14023          {
14024            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14025              image->colormap[i].red) & 0xff);
14026            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14027              image->colormap[i].green) & 0xff);
14028            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14029              image->colormap[i].blue) & 0xff);
14030          }
14031 
14032          (void) WriteBlob(image,data_length+4,chunk);
14033          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14034          mng_info->have_write_global_plte=MagickTrue;
14035        }
14036 #endif
14037     }
14038   scene=0;
14039   mng_info->delay=0;
14040 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14041     defined(PNG_MNG_FEATURES_SUPPORTED)
14042   mng_info->equal_palettes=MagickFalse;
14043 #endif
14044   imageListLength=GetImageListLength(image);
14045   do
14046   {
14047     if (mng_info->adjoin)
14048     {
14049 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14050     defined(PNG_MNG_FEATURES_SUPPORTED)
14051     /*
14052       If we aren't using a global palette for the entire MNG, check to
14053       see if we can use one for two or more consecutive images.
14054     */
14055     if (need_local_plte && use_global_plte && !all_images_are_gray)
14056       {
14057         if (mng_info->IsPalette)
14058           {
14059             /*
14060               When equal_palettes is true, this image has the same palette
14061               as the previous PseudoClass image
14062             */
14063             mng_info->have_write_global_plte=mng_info->equal_palettes;
14064             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14065             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14066               {
14067                 /*
14068                   Write MNG PLTE chunk
14069                 */
14070                 size_t
14071                   data_length;
14072 
14073                 data_length=3*image->colors;
14074                 (void) WriteBlobMSBULong(image,data_length);
14075                 PNGType(chunk,mng_PLTE);
14076                 LogPNGChunk(logging,mng_PLTE,data_length);
14077 
14078                 for (i=0; i < (ssize_t) image->colors; i++)
14079                 {
14080                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14081                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14082                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14083                 }
14084 
14085                 (void) WriteBlob(image,data_length+4,chunk);
14086                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14087                    (uInt) (data_length+4)));
14088                 mng_info->have_write_global_plte=MagickTrue;
14089               }
14090           }
14091         else
14092           mng_info->have_write_global_plte=MagickFalse;
14093       }
14094 #endif
14095     if (need_defi)
14096       {
14097         ssize_t
14098           previous_x,
14099           previous_y;
14100 
14101         if (scene != 0)
14102           {
14103             previous_x=mng_info->page.x;
14104             previous_y=mng_info->page.y;
14105           }
14106         else
14107           {
14108             previous_x=0;
14109             previous_y=0;
14110           }
14111         mng_info->page=image->page;
14112         if ((mng_info->page.x !=  previous_x) ||
14113             (mng_info->page.y != previous_y))
14114           {
14115              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
14116              PNGType(chunk,mng_DEFI);
14117              LogPNGChunk(logging,mng_DEFI,12L);
14118              chunk[4]=0; /* object 0 MSB */
14119              chunk[5]=0; /* object 0 LSB */
14120              chunk[6]=0; /* visible  */
14121              chunk[7]=0; /* abstract */
14122              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14123              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14124              (void) WriteBlob(image,16,chunk);
14125              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14126           }
14127       }
14128     }
14129 
14130    mng_info->write_mng=write_mng;
14131 
14132    if ((int) image->dispose >= 3)
14133      mng_info->framing_mode=3;
14134 
14135    if (mng_info->need_fram && mng_info->adjoin &&
14136        ((image->delay != mng_info->delay) ||
14137         (mng_info->framing_mode != mng_info->old_framing_mode)))
14138      {
14139        if (image->delay == mng_info->delay)
14140          {
14141            /*
14142              Write a MNG FRAM chunk with the new framing mode.
14143            */
14144            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
14145            PNGType(chunk,mng_FRAM);
14146            LogPNGChunk(logging,mng_FRAM,1L);
14147            chunk[4]=(unsigned char) mng_info->framing_mode;
14148            (void) WriteBlob(image,5,chunk);
14149            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14150          }
14151        else
14152          {
14153            /*
14154              Write a MNG FRAM chunk with the delay.
14155            */
14156            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
14157            PNGType(chunk,mng_FRAM);
14158            LogPNGChunk(logging,mng_FRAM,10L);
14159            chunk[4]=(unsigned char) mng_info->framing_mode;
14160            chunk[5]=0;  /* frame name separator (no name) */
14161            chunk[6]=2;  /* flag for changing default delay */
14162            chunk[7]=0;  /* flag for changing frame timeout */
14163            chunk[8]=0;  /* flag for changing frame clipping */
14164            chunk[9]=0;  /* flag for changing frame sync_id */
14165            PNGLong(chunk+10,(png_uint_32)
14166              ((mng_info->ticks_per_second*
14167              image->delay)/MagickMax(image->ticks_per_second,1)));
14168            (void) WriteBlob(image,14,chunk);
14169            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14170            mng_info->delay=(png_uint_32) image->delay;
14171          }
14172        mng_info->old_framing_mode=mng_info->framing_mode;
14173      }
14174 
14175 #if defined(JNG_SUPPORTED)
14176    if (image_info->compression == JPEGCompression)
14177      {
14178        ImageInfo
14179          *write_info;
14180 
14181        if (logging != MagickFalse)
14182          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14183            "  Writing JNG object.");
14184        /* To do: specify the desired alpha compression method. */
14185        write_info=CloneImageInfo(image_info);
14186        write_info->compression=UndefinedCompression;
14187        status=WriteOneJNGImage(mng_info,write_info,image,exception);
14188        write_info=DestroyImageInfo(write_info);
14189      }
14190    else
14191 #endif
14192      {
14193        if (logging != MagickFalse)
14194          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14195            "  Writing PNG object.");
14196 
14197        mng_info->need_blob = MagickFalse;
14198        mng_info->ping_preserve_colormap = MagickFalse;
14199 
14200        /* We don't want any ancillary chunks written */
14201        mng_info->ping_exclude_bKGD=MagickTrue;
14202        mng_info->ping_exclude_caNv=MagickTrue;
14203        mng_info->ping_exclude_cHRM=MagickTrue;
14204        mng_info->ping_exclude_date=MagickTrue;
14205        mng_info->ping_exclude_EXIF=MagickTrue;
14206        mng_info->ping_exclude_gAMA=MagickTrue;
14207        mng_info->ping_exclude_iCCP=MagickTrue;
14208        /* mng_info->ping_exclude_iTXt=MagickTrue; */
14209        mng_info->ping_exclude_oFFs=MagickTrue;
14210        mng_info->ping_exclude_pHYs=MagickTrue;
14211        mng_info->ping_exclude_sRGB=MagickTrue;
14212        mng_info->ping_exclude_tEXt=MagickTrue;
14213        mng_info->ping_exclude_tRNS=MagickTrue;
14214        mng_info->ping_exclude_zCCP=MagickTrue;
14215        mng_info->ping_exclude_zTXt=MagickTrue;
14216 
14217        status=WriteOnePNGImage(mng_info,image_info,image,exception);
14218      }
14219 
14220     if (status == MagickFalse)
14221       {
14222         mng_info=MngInfoFreeStruct(mng_info);
14223         (void) CloseBlob(image);
14224         return(MagickFalse);
14225       }
14226     (void) CatchImageException(image);
14227     if (GetNextImageInList(image) == (Image *) NULL)
14228       break;
14229     image=SyncNextImageInList(image);
14230     status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
14231 
14232     if (status == MagickFalse)
14233       break;
14234 
14235   } while (mng_info->adjoin);
14236 
14237   if (write_mng)
14238     {
14239       while (GetPreviousImageInList(image) != (Image *) NULL)
14240         image=GetPreviousImageInList(image);
14241       /*
14242         Write the MEND chunk.
14243       */
14244       (void) WriteBlobMSBULong(image,0x00000000L);
14245       PNGType(chunk,mng_MEND);
14246       LogPNGChunk(logging,mng_MEND,0L);
14247       (void) WriteBlob(image,4,chunk);
14248       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14249     }
14250   /*
14251     Relinquish resources.
14252   */
14253   (void) CloseBlob(image);
14254   mng_info=MngInfoFreeStruct(mng_info);
14255 
14256   if (logging != MagickFalse)
14257     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14258 
14259   return(MagickTrue);
14260 }
14261 #else /* PNG_LIBPNG_VER > 10011 */
14262 
WritePNGImage(const ImageInfo * image_info,Image * image)14263 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14264   Image *image)
14265 {
14266   (void) image;
14267   printf("Your PNG library is too old: You have libpng-%s\n",
14268      PNG_LIBPNG_VER_STRING);
14269 
14270   ThrowBinaryException(CoderError,"PNG library is too old",
14271      image_info->filename);
14272 }
14273 
WriteMNGImage(const ImageInfo * image_info,Image * image)14274 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14275   Image *image)
14276 {
14277   return(WritePNGImage(image_info,image));
14278 }
14279 #endif /* PNG_LIBPNG_VER > 10011 */
14280 #endif
14281