• 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-2016 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 %    http://www.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 
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
88 
89 /* Suppress libpng pedantic warnings that were added in
90  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
91  * migration to libpng-1.5, remove these defines and then
92  * fix any code that generates warnings.
93  */
94 /* #define PNG_DEPRECATED   Use of this function is deprecated */
95 /* #define PNG_USE_RESULT   The result of this function must be checked */
96 /* #define PNG_NORETURN     This function does not return */
97 /* #define PNG_ALLOCATED    The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
99 
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
102 
103 #include "png.h"
104 #include "zlib.h"
105 
106 /* ImageMagick differences */
107 #define first_scene scene
108 
109 #if PNG_LIBPNG_VER > 10011
110 /*
111   Optional declarations. Define or undefine them as you like.
112 */
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114 
115 /*
116   Features under construction.  Define these to work on them.
117 */
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
124 #endif
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127        (((color).red == (target).red) && \
128         ((color).green == (target).green) && \
129         ((color).blue == (target).blue))
130 #endif
131 
132 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
134 {
135     png_uint_32 len;
136     png_uint_32 crc;
137     png_byte intent;
138 };
139 
140 const struct sRGB_info_struct sRGB_info[] =
141 {
142     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143     { 3048, 0x3b8772b9UL, 0},
144 
145     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146     { 3052, 0x427ebb21UL, 1},
147 
148     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149     {60988, 0x306fd8aeUL, 0},
150 
151     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152      {60960, 0xbbef7812UL, 0},
153 
154     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155      { 3024, 0x5d5129ceUL, 1},
156 
157      /* HP-Microsoft sRGB v2 perceptual */
158      { 3144, 0x182ea552UL, 0},
159 
160      /* HP-Microsoft sRGB v2 media-relative */
161      { 3144, 0xf29e526dUL, 1},
162 
163      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164      {  524, 0xd4938c39UL, 0},
165 
166      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167      { 3212, 0x034af5a1UL, 0},
168 
169      /* Not recognized */
170      {    0, 0x00000000UL, 0},
171 };
172 
173 /* Macros for left-bit-replication to ensure that pixels
174  * and PixelInfos all have the same image->depth, and for use
175  * in PNG8 quantization.
176  */
177 
178 /* LBR01: Replicate top bit */
179 
180 #define LBR01PacketRed(pixelpacket) \
181      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182         0 : QuantumRange);
183 
184 #define LBR01PacketGreen(pixelpacket) \
185      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186         0 : QuantumRange);
187 
188 #define LBR01PacketBlue(pixelpacket) \
189      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190         0 : QuantumRange);
191 
192 #define LBR01PacketAlpha(pixelpacket) \
193      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
194         0 : QuantumRange);
195 
196 #define LBR01PacketRGB(pixelpacket) \
197         { \
198         LBR01PacketRed((pixelpacket)); \
199         LBR01PacketGreen((pixelpacket)); \
200         LBR01PacketBlue((pixelpacket)); \
201         }
202 
203 #define LBR01PacketRGBO(pixelpacket) \
204         { \
205         LBR01PacketRGB((pixelpacket)); \
206         LBR01PacketAlpha((pixelpacket)); \
207         }
208 
209 #define LBR01PixelRed(pixel) \
210         (SetPixelRed(image, \
211         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212         0 : QuantumRange,(pixel)));
213 
214 #define LBR01PixelGreen(pixel) \
215         (SetPixelGreen(image, \
216         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217         0 : QuantumRange,(pixel)));
218 
219 #define LBR01PixelBlue(pixel) \
220         (SetPixelBlue(image, \
221         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222         0 : QuantumRange,(pixel)));
223 
224 #define LBR01PixelAlpha(pixel) \
225         (SetPixelAlpha(image, \
226         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227         0 : QuantumRange,(pixel)));
228 
229 #define LBR01PixelRGB(pixel) \
230         { \
231         LBR01PixelRed((pixel)); \
232         LBR01PixelGreen((pixel)); \
233         LBR01PixelBlue((pixel)); \
234         }
235 
236 #define LBR01PixelRGBA(pixel) \
237         { \
238         LBR01PixelRGB((pixel)); \
239         LBR01PixelAlpha((pixel)); \
240         }
241 
242 /* LBR02: Replicate top 2 bits */
243 
244 #define LBR02PacketRed(pixelpacket) \
245    { \
246      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247      (pixelpacket).red=ScaleCharToQuantum( \
248        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249    }
250 #define LBR02PacketGreen(pixelpacket) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253      (pixelpacket).green=ScaleCharToQuantum( \
254        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255    }
256 #define LBR02PacketBlue(pixelpacket) \
257    { \
258      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259      (pixelpacket).blue=ScaleCharToQuantum( \
260        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261    }
262 #define LBR02PacketAlpha(pixelpacket) \
263    { \
264      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265      (pixelpacket).alpha=ScaleCharToQuantum( \
266        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267    }
268 
269 #define LBR02PacketRGB(pixelpacket) \
270         { \
271         LBR02PacketRed((pixelpacket)); \
272         LBR02PacketGreen((pixelpacket)); \
273         LBR02PacketBlue((pixelpacket)); \
274         }
275 
276 #define LBR02PacketRGBO(pixelpacket) \
277         { \
278         LBR02PacketRGB((pixelpacket)); \
279         LBR02PacketAlpha((pixelpacket)); \
280         }
281 
282 #define LBR02PixelRed(pixel) \
283    { \
284      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
285        & 0xc0; \
286      SetPixelRed(image, ScaleCharToQuantum( \
287        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288        (pixel)); \
289    }
290 #define LBR02PixelGreen(pixel) \
291    { \
292      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
293        & 0xc0; \
294      SetPixelGreen(image, ScaleCharToQuantum( \
295        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296        (pixel)); \
297    }
298 #define LBR02PixelBlue(pixel) \
299    { \
300      unsigned char lbr_bits= \
301        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302      SetPixelBlue(image, ScaleCharToQuantum( \
303        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304        (pixel)); \
305    }
306 #define LBR02PixelAlpha(pixel) \
307    { \
308      unsigned char lbr_bits= \
309        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310      SetPixelAlpha(image, ScaleCharToQuantum( \
311        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312        (pixel) ); \
313    }
314 
315 #define LBR02PixelRGB(pixel) \
316         { \
317         LBR02PixelRed((pixel)); \
318         LBR02PixelGreen((pixel)); \
319         LBR02PixelBlue((pixel)); \
320         }
321 
322 #define LBR02PixelRGBA(pixel) \
323         { \
324         LBR02PixelRGB((pixel)); \
325         LBR02PixelAlpha((pixel)); \
326         }
327 
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329    PNG8 quantization) */
330 
331 #define LBR03PacketRed(pixelpacket) \
332    { \
333      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334      (pixelpacket).red=ScaleCharToQuantum( \
335        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336    }
337 #define LBR03PacketGreen(pixelpacket) \
338    { \
339      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340      (pixelpacket).green=ScaleCharToQuantum( \
341        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342    }
343 #define LBR03PacketBlue(pixelpacket) \
344    { \
345      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346      (pixelpacket).blue=ScaleCharToQuantum( \
347        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348    }
349 
350 #define LBR03PacketRGB(pixelpacket) \
351         { \
352         LBR03PacketRed((pixelpacket)); \
353         LBR03PacketGreen((pixelpacket)); \
354         LBR03PacketBlue((pixelpacket)); \
355         }
356 
357 #define LBR03PixelRed(pixel) \
358    { \
359      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
360        & 0xe0; \
361      SetPixelRed(image, ScaleCharToQuantum( \
362        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
363    }
364 #define LBR03Green(pixel) \
365    { \
366      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
367        & 0xe0; \
368      SetPixelGreen(image, ScaleCharToQuantum( \
369        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
370    }
371 #define LBR03Blue(pixel) \
372    { \
373      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
374        & 0xe0; \
375      SetPixelBlue(image, ScaleCharToQuantum( \
376        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
377    }
378 
379 #define LBR03RGB(pixel) \
380         { \
381         LBR03PixelRed((pixel)); \
382         LBR03Green((pixel)); \
383         LBR03Blue((pixel)); \
384         }
385 
386 /* LBR04: Replicate top 4 bits */
387 
388 #define LBR04PacketRed(pixelpacket) \
389    { \
390      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392    }
393 #define LBR04PacketGreen(pixelpacket) \
394    { \
395      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397    }
398 #define LBR04PacketBlue(pixelpacket) \
399    { \
400      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402    }
403 #define LBR04PacketAlpha(pixelpacket) \
404    { \
405      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407    }
408 
409 #define LBR04PacketRGB(pixelpacket) \
410         { \
411         LBR04PacketRed((pixelpacket)); \
412         LBR04PacketGreen((pixelpacket)); \
413         LBR04PacketBlue((pixelpacket)); \
414         }
415 
416 #define LBR04PacketRGBO(pixelpacket) \
417         { \
418         LBR04PacketRGB((pixelpacket)); \
419         LBR04PacketAlpha((pixelpacket)); \
420         }
421 
422 #define LBR04PixelRed(pixel) \
423    { \
424      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
425        & 0xf0; \
426      SetPixelRed(image,\
427        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
428    }
429 #define LBR04PixelGreen(pixel) \
430    { \
431      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
432        & 0xf0; \
433      SetPixelGreen(image,\
434        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
435    }
436 #define LBR04PixelBlue(pixel) \
437    { \
438      unsigned char lbr_bits= \
439        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440      SetPixelBlue(image,\
441        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
442    }
443 #define LBR04PixelAlpha(pixel) \
444    { \
445      unsigned char lbr_bits= \
446        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447      SetPixelAlpha(image,\
448        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
449    }
450 
451 #define LBR04PixelRGB(pixel) \
452         { \
453         LBR04PixelRed((pixel)); \
454         LBR04PixelGreen((pixel)); \
455         LBR04PixelBlue((pixel)); \
456         }
457 
458 #define LBR04PixelRGBA(pixel) \
459         { \
460         LBR04PixelRGB((pixel)); \
461         LBR04PixelAlpha((pixel)); \
462         }
463 
464 /*
465   Establish thread safety.
466   setjmp/longjmp is claimed to be safe on these platforms:
467   setjmp/longjmp is alleged to be unsafe on these platforms:
468 */
469 #ifdef PNG_SETJMP_SUPPORTED
470 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
471 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
472 # endif
473 
474 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
475 static SemaphoreInfo
476   *ping_semaphore = (SemaphoreInfo *) NULL;
477 # endif
478 #endif
479 
480 /*
481   This temporary until I set up malloc'ed object attributes array.
482   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
483   waste more memory.
484 */
485 #define MNG_MAX_OBJECTS 256
486 
487 /*
488   If this not defined, spec is interpreted strictly.  If it is
489   defined, an attempt will be made to recover from some errors,
490   including
491       o global PLTE too short
492 */
493 #undef MNG_LOOSE
494 
495 /*
496   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
497   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
498   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
499   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
500   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
501   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
502   will be enabled by default in libpng-1.2.0.
503 */
504 #ifdef PNG_MNG_FEATURES_SUPPORTED
505 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
506 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
507 #  endif
508 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
509 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
510 #  endif
511 #endif
512 
513 /*
514   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
515   This macro is only defined in libpng-1.0.3 and later.
516   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
517 */
518 #ifndef PNG_UINT_31_MAX
519 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
520 #endif
521 
522 /*
523   Constant strings for known chunk types.  If you need to add a chunk,
524   add a string holding the name here.   To make the code more
525   portable, we use ASCII numbers like this, not characters.
526 */
527 
528 static const png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
529 static const png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
530 static const png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
531 static const png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
532 static const png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
533 static const png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
534 static const png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
535 static const png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
536 static const png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
537 static const png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
538 static const png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
539 static const png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
540 static const png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
541 static const png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
542 static const png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
543 static const png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
544 static const png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
545 static const png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
546 static const png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
547 static const png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
548 static const png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
549 static const png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
550 static const png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
551 static const png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
552 static const png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
553 static const png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
554 static const png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
555 static const png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
556 static const png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
557 static const png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
558 static const png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
559 static const png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
560 static const png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
561 static const png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
562 
563 #if defined(JNG_SUPPORTED)
564 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
565 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
566 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
567 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
568 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
569 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
570 #endif
571 
572 #if 0
573 /* Other known chunks that are not yet supported by ImageMagick: */
574 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
575 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
576 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
577 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
578 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
579 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
580 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
581 #endif
582 
583 typedef struct _MngBox
584 {
585   long
586     left,
587     right,
588     top,
589     bottom;
590 } MngBox;
591 
592 typedef struct _MngPair
593 {
594   volatile long
595     a,
596     b;
597 } MngPair;
598 
599 #ifdef MNG_OBJECT_BUFFERS
600 typedef struct _MngBuffer
601 {
602 
603   size_t
604     height,
605     width;
606 
607   Image
608     *image;
609 
610   png_color
611     plte[256];
612 
613   int
614     reference_count;
615 
616   unsigned char
617     alpha_sample_depth,
618     compression_method,
619     color_type,
620     concrete,
621     filter_method,
622     frozen,
623     image_type,
624     interlace_method,
625     pixel_sample_depth,
626     plte_length,
627     sample_depth,
628     viewable;
629 } MngBuffer;
630 #endif
631 
632 typedef struct _MngInfo
633 {
634 
635 #ifdef MNG_OBJECT_BUFFERS
636   MngBuffer
637     *ob[MNG_MAX_OBJECTS];
638 #endif
639 
640   Image *
641     image;
642 
643   RectangleInfo
644     page;
645 
646   int
647     adjoin,
648 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
649     bytes_in_read_buffer,
650     found_empty_plte,
651 #endif
652     equal_backgrounds,
653     equal_chrms,
654     equal_gammas,
655 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
656     defined(PNG_MNG_FEATURES_SUPPORTED)
657     equal_palettes,
658 #endif
659     equal_physs,
660     equal_srgbs,
661     framing_mode,
662     have_global_bkgd,
663     have_global_chrm,
664     have_global_gama,
665     have_global_phys,
666     have_global_sbit,
667     have_global_srgb,
668     have_saved_bkgd_index,
669     have_write_global_chrm,
670     have_write_global_gama,
671     have_write_global_plte,
672     have_write_global_srgb,
673     need_fram,
674     object_id,
675     old_framing_mode,
676     saved_bkgd_index;
677 
678   int
679     new_number_colors;
680 
681   ssize_t
682     image_found,
683     loop_count[256],
684     loop_iteration[256],
685     scenes_found,
686     x_off[MNG_MAX_OBJECTS],
687     y_off[MNG_MAX_OBJECTS];
688 
689   MngBox
690     clip,
691     frame,
692     image_box,
693     object_clip[MNG_MAX_OBJECTS];
694 
695   unsigned char
696     /* These flags could be combined into one byte */
697     exists[MNG_MAX_OBJECTS],
698     frozen[MNG_MAX_OBJECTS],
699     loop_active[256],
700     invisible[MNG_MAX_OBJECTS],
701     viewable[MNG_MAX_OBJECTS];
702 
703   MagickOffsetType
704     loop_jump[256];
705 
706   png_colorp
707     global_plte;
708 
709   png_color_8
710     global_sbit;
711 
712   png_byte
713 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
714     read_buffer[8],
715 #endif
716     global_trns[256];
717 
718   float
719     global_gamma;
720 
721   ChromaticityInfo
722     global_chrm;
723 
724   RenderingIntent
725     global_srgb_intent;
726 
727   unsigned int
728     delay,
729     global_plte_length,
730     global_trns_length,
731     global_x_pixels_per_unit,
732     global_y_pixels_per_unit,
733     mng_width,
734     mng_height,
735     ticks_per_second;
736 
737   MagickBooleanType
738     need_blob;
739 
740   unsigned int
741     IsPalette,
742     global_phys_unit_type,
743     basi_warning,
744     clon_warning,
745     dhdr_warning,
746     jhdr_warning,
747     magn_warning,
748     past_warning,
749     phyg_warning,
750     phys_warning,
751     sbit_warning,
752     show_warning,
753     mng_type,
754     write_mng,
755     write_png_colortype,
756     write_png_depth,
757     write_png_compression_level,
758     write_png_compression_strategy,
759     write_png_compression_filter,
760     write_png8,
761     write_png24,
762     write_png32,
763     write_png48,
764     write_png64;
765 
766 #ifdef MNG_BASI_SUPPORTED
767   size_t
768     basi_width,
769     basi_height;
770 
771   unsigned int
772     basi_depth,
773     basi_color_type,
774     basi_compression_method,
775     basi_filter_type,
776     basi_interlace_method,
777     basi_red,
778     basi_green,
779     basi_blue,
780     basi_alpha,
781     basi_viewable;
782 #endif
783 
784   png_uint_16
785     magn_first,
786     magn_last,
787     magn_mb,
788     magn_ml,
789     magn_mr,
790     magn_mt,
791     magn_mx,
792     magn_my,
793     magn_methx,
794     magn_methy;
795 
796   PixelInfo
797     mng_global_bkgd;
798 
799   /* Added at version 6.6.6-7 */
800   MagickBooleanType
801     ping_exclude_bKGD,
802     ping_exclude_cHRM,
803     ping_exclude_date,
804     ping_exclude_EXIF,
805     ping_exclude_gAMA,
806     ping_exclude_iCCP,
807     /* ping_exclude_iTXt, */
808     ping_exclude_oFFs,
809     ping_exclude_pHYs,
810     ping_exclude_sRGB,
811     ping_exclude_tEXt,
812     ping_exclude_tRNS,
813     ping_exclude_vpAg,
814     ping_exclude_zCCP, /* hex-encoded iCCP */
815     ping_exclude_zTXt,
816     ping_preserve_colormap,
817   /* Added at version 6.8.5-7 */
818     ping_preserve_iCCP,
819   /* Added at version 6.8.9-9 */
820     ping_exclude_tIME;
821 
822 } MngInfo;
823 #endif /* VER */
824 
825 /*
826   Forward declarations.
827 */
828 static MagickBooleanType
829   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
830 
831 static MagickBooleanType
832   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
833 
834 #if defined(JNG_SUPPORTED)
835 static MagickBooleanType
836   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
837 #endif
838 
839 #if PNG_LIBPNG_VER > 10011
840 
841 
842 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
843 static MagickBooleanType
LosslessReduceDepthOK(Image * image,ExceptionInfo * exception)844 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
845 {
846     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
847      *
848      * This is true if the high byte and the next highest byte of
849      * each sample of the image, the colormap, and the background color
850      * are equal to each other.  We check this by seeing if the samples
851      * are unchanged when we scale them down to 8 and back up to Quantum.
852      *
853      * We don't use the method GetImageDepth() because it doesn't check
854      * background and doesn't handle PseudoClass specially.
855      */
856 
857 #define QuantumToCharToQuantumEqQuantum(quantum) \
858   ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
859 
860     MagickBooleanType
861       ok_to_reduce=MagickFalse;
862 
863     if (image->depth >= 16)
864       {
865 
866         const Quantum
867           *p;
868 
869         ok_to_reduce=
870            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
871            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
872            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
873            MagickTrue : MagickFalse;
874 
875         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
876           {
877             int indx;
878 
879             for (indx=0; indx < (ssize_t) image->colors; indx++)
880               {
881                 ok_to_reduce=(
882                    QuantumToCharToQuantumEqQuantum(
883                    image->colormap[indx].red) &&
884                    QuantumToCharToQuantumEqQuantum(
885                    image->colormap[indx].green) &&
886                    QuantumToCharToQuantumEqQuantum(
887                    image->colormap[indx].blue)) ?
888                    MagickTrue : MagickFalse;
889 
890                 if (ok_to_reduce == MagickFalse)
891                    break;
892               }
893           }
894 
895         if ((ok_to_reduce != MagickFalse) &&
896             (image->storage_class != PseudoClass))
897           {
898             ssize_t
899               y;
900 
901             register ssize_t
902               x;
903 
904             for (y=0; y < (ssize_t) image->rows; y++)
905             {
906               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
907 
908               if (p == (const Quantum *) NULL)
909                 {
910                   ok_to_reduce = MagickFalse;
911                   break;
912                 }
913 
914               for (x=(ssize_t) image->columns-1; x >= 0; x--)
915               {
916                 ok_to_reduce=
917                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
918                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
919                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
920                    MagickTrue : MagickFalse;
921 
922                 if (ok_to_reduce == MagickFalse)
923                   break;
924 
925                 p+=GetPixelChannels(image);
926               }
927               if (x >= 0)
928                 break;
929             }
930           }
931 
932         if (ok_to_reduce != MagickFalse)
933           {
934             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
935                 "    OK to reduce PNG bit depth to 8 without loss of info");
936           }
937         else
938           {
939             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
940                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
941           }
942       }
943 
944     return ok_to_reduce;
945 }
946 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
947 
PngColorTypeToString(const unsigned int color_type)948 static const char* PngColorTypeToString(const unsigned int color_type)
949 {
950   const char
951     *result = "Unknown";
952 
953   switch (color_type)
954     {
955     case PNG_COLOR_TYPE_GRAY:
956       result = "Gray";
957       break;
958     case PNG_COLOR_TYPE_GRAY_ALPHA:
959       result = "Gray+Alpha";
960       break;
961     case PNG_COLOR_TYPE_PALETTE:
962       result = "Palette";
963       break;
964     case PNG_COLOR_TYPE_RGB:
965       result = "RGB";
966       break;
967     case PNG_COLOR_TYPE_RGB_ALPHA:
968       result = "RGB+Alpha";
969       break;
970     }
971 
972   return result;
973 }
974 
975 static int
Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)976 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
977 {
978   switch (intent)
979   {
980     case PerceptualIntent:
981        return 0;
982 
983     case RelativeIntent:
984        return 1;
985 
986     case SaturationIntent:
987        return 2;
988 
989     case AbsoluteIntent:
990        return 3;
991 
992     default:
993        return -1;
994   }
995 }
996 
997 static RenderingIntent
Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)998 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
999 {
1000   switch (ping_intent)
1001   {
1002     case 0:
1003       return PerceptualIntent;
1004 
1005     case 1:
1006       return RelativeIntent;
1007 
1008     case 2:
1009       return SaturationIntent;
1010 
1011     case 3:
1012       return AbsoluteIntent;
1013 
1014     default:
1015       return UndefinedIntent;
1016     }
1017 }
1018 
1019 static const char *
Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)1020 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1021 {
1022   switch (ping_intent)
1023   {
1024     case 0:
1025       return "Perceptual Intent";
1026 
1027     case 1:
1028       return "Relative Intent";
1029 
1030     case 2:
1031       return "Saturation Intent";
1032 
1033     case 3:
1034       return "Absolute Intent";
1035 
1036     default:
1037       return "Undefined Intent";
1038     }
1039 }
1040 
1041 static const char *
Magick_ColorType_from_PNG_ColorType(const int ping_colortype)1042 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1043 {
1044   switch (ping_colortype)
1045   {
1046     case 0:
1047       return "Grayscale";
1048 
1049     case 2:
1050       return "Truecolor";
1051 
1052     case 3:
1053       return "Indexed";
1054 
1055     case 4:
1056       return "GrayAlpha";
1057 
1058     case 6:
1059       return "RGBA";
1060 
1061     default:
1062       return "UndefinedColorType";
1063     }
1064 }
1065 
1066 #endif /* PNG_LIBPNG_VER > 10011 */
1067 #endif /* MAGICKCORE_PNG_DELEGATE */
1068 
1069 /*
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 %                                                                             %
1072 %                                                                             %
1073 %                                                                             %
1074 %   I s M N G                                                                 %
1075 %                                                                             %
1076 %                                                                             %
1077 %                                                                             %
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079 %
1080 %  IsMNG() returns MagickTrue if the image format type, identified by the
1081 %  magick string, is MNG.
1082 %
1083 %  The format of the IsMNG method is:
1084 %
1085 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1086 %
1087 %  A description of each parameter follows:
1088 %
1089 %    o magick: compare image format pattern against these bytes.
1090 %
1091 %    o length: Specifies the length of the magick string.
1092 %
1093 %
1094 */
IsMNG(const unsigned char * magick,const size_t length)1095 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1096 {
1097   if (length < 8)
1098     return(MagickFalse);
1099 
1100   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1101     return(MagickTrue);
1102 
1103   return(MagickFalse);
1104 }
1105 
1106 /*
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108 %                                                                             %
1109 %                                                                             %
1110 %                                                                             %
1111 %   I s J N G                                                                 %
1112 %                                                                             %
1113 %                                                                             %
1114 %                                                                             %
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116 %
1117 %  IsJNG() returns MagickTrue if the image format type, identified by the
1118 %  magick string, is JNG.
1119 %
1120 %  The format of the IsJNG method is:
1121 %
1122 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1123 %
1124 %  A description of each parameter follows:
1125 %
1126 %    o magick: compare image format pattern against these bytes.
1127 %
1128 %    o length: Specifies the length of the magick string.
1129 %
1130 %
1131 */
IsJNG(const unsigned char * magick,const size_t length)1132 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1133 {
1134   if (length < 8)
1135     return(MagickFalse);
1136 
1137   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1138     return(MagickTrue);
1139 
1140   return(MagickFalse);
1141 }
1142 
1143 /*
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1145 %                                                                             %
1146 %                                                                             %
1147 %                                                                             %
1148 %   I s P N G                                                                 %
1149 %                                                                             %
1150 %                                                                             %
1151 %                                                                             %
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1153 %
1154 %  IsPNG() returns MagickTrue if the image format type, identified by the
1155 %  magick string, is PNG.
1156 %
1157 %  The format of the IsPNG method is:
1158 %
1159 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1160 %
1161 %  A description of each parameter follows:
1162 %
1163 %    o magick: compare image format pattern against these bytes.
1164 %
1165 %    o length: Specifies the length of the magick string.
1166 %
1167 */
IsPNG(const unsigned char * magick,const size_t length)1168 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1169 {
1170   if (length < 8)
1171     return(MagickFalse);
1172 
1173   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1174     return(MagickTrue);
1175 
1176   return(MagickFalse);
1177 }
1178 
1179 #if defined(MAGICKCORE_PNG_DELEGATE)
1180 #if defined(__cplusplus) || defined(c_plusplus)
1181 extern "C" {
1182 #endif
1183 
1184 #if (PNG_LIBPNG_VER > 10011)
WriteBlobMSBULong(Image * image,const size_t value)1185 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1186 {
1187   unsigned char
1188     buffer[4];
1189 
1190   assert(image != (Image *) NULL);
1191   assert(image->signature == MagickCoreSignature);
1192   buffer[0]=(unsigned char) (value >> 24);
1193   buffer[1]=(unsigned char) (value >> 16);
1194   buffer[2]=(unsigned char) (value >> 8);
1195   buffer[3]=(unsigned char) value;
1196   return((size_t) WriteBlob(image,4,buffer));
1197 }
1198 
PNGLong(png_bytep p,png_uint_32 value)1199 static void PNGLong(png_bytep p,png_uint_32 value)
1200 {
1201   *p++=(png_byte) ((value >> 24) & 0xff);
1202   *p++=(png_byte) ((value >> 16) & 0xff);
1203   *p++=(png_byte) ((value >> 8) & 0xff);
1204   *p++=(png_byte) (value & 0xff);
1205 }
1206 
1207 #if defined(JNG_SUPPORTED)
PNGsLong(png_bytep p,png_int_32 value)1208 static void PNGsLong(png_bytep p,png_int_32 value)
1209 {
1210   *p++=(png_byte) ((value >> 24) & 0xff);
1211   *p++=(png_byte) ((value >> 16) & 0xff);
1212   *p++=(png_byte) ((value >> 8) & 0xff);
1213   *p++=(png_byte) (value & 0xff);
1214 }
1215 #endif
1216 
PNGShort(png_bytep p,png_uint_16 value)1217 static void PNGShort(png_bytep p,png_uint_16 value)
1218 {
1219   *p++=(png_byte) ((value >> 8) & 0xff);
1220   *p++=(png_byte) (value & 0xff);
1221 }
1222 
PNGType(png_bytep p,const png_byte * type)1223 static void PNGType(png_bytep p,const png_byte *type)
1224 {
1225   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1226 }
1227 
LogPNGChunk(MagickBooleanType logging,const png_byte * type,size_t length)1228 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1229    size_t length)
1230 {
1231   if (logging != MagickFalse)
1232     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1233       "  Writing %c%c%c%c chunk, length: %.20g",
1234       type[0],type[1],type[2],type[3],(double) length);
1235 }
1236 #endif /* PNG_LIBPNG_VER > 10011 */
1237 
1238 #if defined(__cplusplus) || defined(c_plusplus)
1239 }
1240 #endif
1241 
1242 #if PNG_LIBPNG_VER > 10011
1243 /*
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245 %                                                                             %
1246 %                                                                             %
1247 %                                                                             %
1248 %   R e a d P N G I m a g e                                                   %
1249 %                                                                             %
1250 %                                                                             %
1251 %                                                                             %
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253 %
1254 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1255 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1256 %  allocates the memory necessary for the new Image structure and returns a
1257 %  pointer to the new image or set of images.
1258 %
1259 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1260 %
1261 %  The format of the ReadPNGImage method is:
1262 %
1263 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1264 %
1265 %  A description of each parameter follows:
1266 %
1267 %    o image_info: the image info.
1268 %
1269 %    o exception: return any errors or warnings in this structure.
1270 %
1271 %  To do, more or less in chronological order (as of version 5.5.2,
1272 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1273 %
1274 %    Get 16-bit cheap transparency working.
1275 %
1276 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1277 %
1278 %    Preserve all unknown and not-yet-handled known chunks found in input
1279 %    PNG file and copy them into output PNG files according to the PNG
1280 %    copying rules.
1281 %
1282 %    (At this point, PNG encoding should be in full MNG compliance)
1283 %
1284 %    Provide options for choice of background to use when the MNG BACK
1285 %    chunk is not present or is not mandatory (i.e., leave transparent,
1286 %    user specified, MNG BACK, PNG bKGD)
1287 %
1288 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1289 %    efficiently by linking in the duplicate frames.].
1290 %
1291 %    Decode and act on the MHDR simplicity profile (offer option to reject
1292 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1293 %
1294 %    Upgrade to full MNG without Delta-PNG.
1295 %
1296 %        o  BACK [done a while ago except for background image ID]
1297 %        o  MOVE [done 15 May 1999]
1298 %        o  CLIP [done 15 May 1999]
1299 %        o  DISC [done 19 May 1999]
1300 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1301 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1302 %        o  SHOW
1303 %        o  PAST
1304 %        o  BASI
1305 %        o  MNG-level tEXt/iTXt/zTXt
1306 %        o  pHYg
1307 %        o  pHYs
1308 %        o  sBIT
1309 %        o  bKGD
1310 %        o  iTXt (wait for libpng implementation).
1311 %
1312 %    Use the scene signature to discover when an identical scene is
1313 %    being reused, and just point to the original image->exception instead
1314 %    of storing another set of pixels.  This not specific to MNG
1315 %    but could be applied generally.
1316 %
1317 %    Upgrade to full MNG with Delta-PNG.
1318 %
1319 %    JNG tEXt/iTXt/zTXt
1320 %
1321 %    We will not attempt to read files containing the CgBI chunk.
1322 %    They are really Xcode files meant for display on the iPhone.
1323 %    These are not valid PNG files and it is impossible to recover
1324 %    the original PNG from files that have been converted to Xcode-PNG,
1325 %    since irretrievable loss of color data has occurred due to the
1326 %    use of premultiplied alpha.
1327 */
1328 
1329 #if defined(__cplusplus) || defined(c_plusplus)
1330 extern "C" {
1331 #endif
1332 
1333 /*
1334   This the function that does the actual reading of data.  It is
1335   the same as the one supplied in libpng, except that it receives the
1336   datastream from the ReadBlob() function instead of standard input.
1337 */
png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1338 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1339 {
1340   Image
1341     *image;
1342 
1343   image=(Image *) png_get_io_ptr(png_ptr);
1344   if (length != 0)
1345     {
1346       png_size_t
1347         check;
1348 
1349       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1350       if (check != length)
1351         {
1352           char
1353             msg[MagickPathExtent];
1354 
1355           (void) FormatLocaleString(msg,MagickPathExtent,
1356             "Expected %.20g bytes; found %.20g bytes",(double) length,
1357             (double) check);
1358           png_warning(png_ptr,msg);
1359           png_error(png_ptr,"Read Exception");
1360         }
1361     }
1362 }
1363 
1364 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1365     !defined(PNG_MNG_FEATURES_SUPPORTED)
1366 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1367  * older than libpng-1.0.3a, which was the first to allow the empty
1368  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1369  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1370  * encountered after an empty PLTE, so we have to look ahead for bKGD
1371  * chunks and remove them from the datastream that is passed to libpng,
1372  * and store their contents for later use.
1373  */
mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1374 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1375 {
1376   MngInfo
1377     *mng_info;
1378 
1379   Image
1380     *image;
1381 
1382   png_size_t
1383     check;
1384 
1385   register ssize_t
1386     i;
1387 
1388   i=0;
1389   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1390   image=(Image *) mng_info->image;
1391   while (mng_info->bytes_in_read_buffer && length)
1392   {
1393     data[i]=mng_info->read_buffer[i];
1394     mng_info->bytes_in_read_buffer--;
1395     length--;
1396     i++;
1397   }
1398   if (length != 0)
1399     {
1400       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1401 
1402       if (check != length)
1403         png_error(png_ptr,"Read Exception");
1404 
1405       if (length == 4)
1406         {
1407           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1408               (data[3] == 0))
1409             {
1410               check=(png_size_t) ReadBlob(image,(size_t) length,
1411                 (char *) mng_info->read_buffer);
1412               mng_info->read_buffer[4]=0;
1413               mng_info->bytes_in_read_buffer=4;
1414               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1415                 mng_info->found_empty_plte=MagickTrue;
1416               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1417                 {
1418                   mng_info->found_empty_plte=MagickFalse;
1419                   mng_info->have_saved_bkgd_index=MagickFalse;
1420                 }
1421             }
1422 
1423           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1424               (data[3] == 1))
1425             {
1426               check=(png_size_t) ReadBlob(image,(size_t) length,
1427                 (char *) mng_info->read_buffer);
1428               mng_info->read_buffer[4]=0;
1429               mng_info->bytes_in_read_buffer=4;
1430               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1431                 if (mng_info->found_empty_plte)
1432                   {
1433                     /*
1434                       Skip the bKGD data byte and CRC.
1435                     */
1436                     check=(png_size_t)
1437                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1438                     check=(png_size_t) ReadBlob(image,(size_t) length,
1439                       (char *) mng_info->read_buffer);
1440                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1441                     mng_info->have_saved_bkgd_index=MagickTrue;
1442                     mng_info->bytes_in_read_buffer=0;
1443                   }
1444             }
1445         }
1446     }
1447 }
1448 #endif
1449 
png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)1450 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1451 {
1452   Image
1453     *image;
1454 
1455   image=(Image *) png_get_io_ptr(png_ptr);
1456   if (length != 0)
1457     {
1458       png_size_t
1459         check;
1460 
1461       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1462 
1463       if (check != length)
1464         png_error(png_ptr,"WriteBlob Failed");
1465     }
1466 }
1467 
png_flush_data(png_structp png_ptr)1468 static void png_flush_data(png_structp png_ptr)
1469 {
1470   (void) png_ptr;
1471 }
1472 
1473 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
PalettesAreEqual(Image * a,Image * b)1474 static int PalettesAreEqual(Image *a,Image *b)
1475 {
1476   ssize_t
1477     i;
1478 
1479   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1480     return((int) MagickFalse);
1481 
1482   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1483     return((int) MagickFalse);
1484 
1485   if (a->colors != b->colors)
1486     return((int) MagickFalse);
1487 
1488   for (i=0; i < (ssize_t) a->colors; i++)
1489   {
1490     if ((a->colormap[i].red != b->colormap[i].red) ||
1491         (a->colormap[i].green != b->colormap[i].green) ||
1492         (a->colormap[i].blue != b->colormap[i].blue))
1493       return((int) MagickFalse);
1494   }
1495 
1496   return((int) MagickTrue);
1497 }
1498 #endif
1499 
MngInfoDiscardObject(MngInfo * mng_info,int i)1500 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1501 {
1502   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1503       mng_info->exists[i] && !mng_info->frozen[i])
1504     {
1505 #ifdef MNG_OBJECT_BUFFERS
1506       if (mng_info->ob[i] != (MngBuffer *) NULL)
1507         {
1508           if (mng_info->ob[i]->reference_count > 0)
1509             mng_info->ob[i]->reference_count--;
1510 
1511           if (mng_info->ob[i]->reference_count == 0)
1512             {
1513               if (mng_info->ob[i]->image != (Image *) NULL)
1514                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1515 
1516               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1517             }
1518         }
1519       mng_info->ob[i]=(MngBuffer *) NULL;
1520 #endif
1521       mng_info->exists[i]=MagickFalse;
1522       mng_info->invisible[i]=MagickFalse;
1523       mng_info->viewable[i]=MagickFalse;
1524       mng_info->frozen[i]=MagickFalse;
1525       mng_info->x_off[i]=0;
1526       mng_info->y_off[i]=0;
1527       mng_info->object_clip[i].left=0;
1528       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1529       mng_info->object_clip[i].top=0;
1530       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1531     }
1532 }
1533 
MngInfoFreeStruct(MngInfo * mng_info,MagickBooleanType * have_mng_structure)1534 static void MngInfoFreeStruct(MngInfo *mng_info,
1535     MagickBooleanType *have_mng_structure)
1536 {
1537   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1538     {
1539       register ssize_t
1540         i;
1541 
1542       for (i=1; i < MNG_MAX_OBJECTS; i++)
1543         MngInfoDiscardObject(mng_info,i);
1544 
1545       if (mng_info->global_plte != (png_colorp) NULL)
1546         mng_info->global_plte=(png_colorp)
1547           RelinquishMagickMemory(mng_info->global_plte);
1548 
1549       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1550       *have_mng_structure=MagickFalse;
1551     }
1552 }
1553 
mng_minimum_box(MngBox box1,MngBox box2)1554 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1555 {
1556   MngBox
1557     box;
1558 
1559   box=box1;
1560   if (box.left < box2.left)
1561     box.left=box2.left;
1562 
1563   if (box.top < box2.top)
1564     box.top=box2.top;
1565 
1566   if (box.right > box2.right)
1567     box.right=box2.right;
1568 
1569   if (box.bottom > box2.bottom)
1570     box.bottom=box2.bottom;
1571 
1572   return box;
1573 }
1574 
mng_read_box(MngBox previous_box,char delta_type,unsigned char * p)1575 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1576 {
1577    MngBox
1578       box;
1579 
1580   /*
1581     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1582   */
1583   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1584   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1585   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1586   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1587   if (delta_type != 0)
1588     {
1589       box.left+=previous_box.left;
1590       box.right+=previous_box.right;
1591       box.top+=previous_box.top;
1592       box.bottom+=previous_box.bottom;
1593     }
1594 
1595   return(box);
1596 }
1597 
mng_read_pair(MngPair previous_pair,int delta_type,unsigned char * p)1598 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1599   unsigned char *p)
1600 {
1601   MngPair
1602     pair;
1603   /*
1604     Read two ssize_ts from CLON, MOVE or PAST chunk
1605   */
1606   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1607   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1608 
1609   if (delta_type != 0)
1610     {
1611       pair.a+=previous_pair.a;
1612       pair.b+=previous_pair.b;
1613     }
1614 
1615   return(pair);
1616 }
1617 
mng_get_long(unsigned char * p)1618 static long mng_get_long(unsigned char *p)
1619 {
1620   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1621 }
1622 
1623 typedef struct _PNGErrorInfo
1624 {
1625   Image
1626     *image;
1627 
1628   ExceptionInfo
1629     *exception;
1630 } PNGErrorInfo;
1631 
MagickPNGErrorHandler(png_struct * ping,png_const_charp message)1632 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1633 {
1634   ExceptionInfo
1635     *exception;
1636 
1637   Image
1638     *image;
1639 
1640   PNGErrorInfo
1641     *error_info;
1642 
1643   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1644   image=error_info->image;
1645   exception=error_info->exception;
1646 
1647   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1648     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1649 
1650   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1651     "`%s'",image->filename);
1652 
1653 #if (PNG_LIBPNG_VER < 10500)
1654   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1655    * are building with libpng-1.4.x and can be ignored.
1656    */
1657   longjmp(ping->jmpbuf,1);
1658 #else
1659   png_longjmp(ping,1);
1660 #endif
1661 }
1662 
MagickPNGWarningHandler(png_struct * ping,png_const_charp message)1663 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1664 {
1665   ExceptionInfo
1666     *exception;
1667 
1668   Image
1669     *image;
1670 
1671   PNGErrorInfo
1672     *error_info;
1673 
1674   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1675     png_error(ping, message);
1676 
1677   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1678   image=error_info->image;
1679   exception=error_info->exception;
1680   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1681     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1682 
1683   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1684     message,"`%s'",image->filename);
1685 }
1686 
1687 #ifdef PNG_USER_MEM_SUPPORTED
1688 #if PNG_LIBPNG_VER >= 10400
Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)1689 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1690 #else
1691 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1692 #endif
1693 {
1694   (void) png_ptr;
1695   return((png_voidp) AcquireMagickMemory((size_t) size));
1696 }
1697 
1698 /*
1699   Free a pointer.  It is removed from the list at the same time.
1700 */
Magick_png_free(png_structp png_ptr,png_voidp ptr)1701 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1702 {
1703   (void) png_ptr;
1704   ptr=RelinquishMagickMemory(ptr);
1705   return((png_free_ptr) NULL);
1706 }
1707 #endif
1708 
1709 #if defined(__cplusplus) || defined(c_plusplus)
1710 }
1711 #endif
1712 
1713 static int
Magick_png_read_raw_profile(png_struct * ping,Image * image,const ImageInfo * image_info,png_textp text,int ii,ExceptionInfo * exception)1714 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1715    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1716 {
1717   register ssize_t
1718     i;
1719 
1720   register unsigned char
1721     *dp;
1722 
1723   register png_charp
1724     sp;
1725 
1726   png_uint_32
1727     length,
1728     nibbles;
1729 
1730   StringInfo
1731     *profile;
1732 
1733   const unsigned char
1734     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1735                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1736                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1737                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1738                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1739                  13,14,15};
1740 
1741   sp=text[ii].text+1;
1742   /* look for newline */
1743   while (*sp != '\n')
1744      sp++;
1745 
1746   /* look for length */
1747   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1748      sp++;
1749 
1750   length=(png_uint_32) StringToLong(sp);
1751 
1752   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1753        "      length: %lu",(unsigned long) length);
1754 
1755   while (*sp != ' ' && *sp != '\n')
1756      sp++;
1757 
1758   /* allocate space */
1759   if (length == 0)
1760   {
1761     png_warning(ping,"invalid profile length");
1762     return(MagickFalse);
1763   }
1764 
1765   profile=BlobToStringInfo((const void *) NULL,length);
1766 
1767   if (profile == (StringInfo *) NULL)
1768   {
1769     png_warning(ping, "unable to copy profile");
1770     return(MagickFalse);
1771   }
1772 
1773   /* copy profile, skipping white space and column 1 "=" signs */
1774   dp=GetStringInfoDatum(profile);
1775   nibbles=length*2;
1776 
1777   for (i=0; i < (ssize_t) nibbles; i++)
1778   {
1779     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1780     {
1781       if (*sp == '\0')
1782         {
1783           png_warning(ping, "ran out of profile data");
1784           profile=DestroyStringInfo(profile);
1785           return(MagickFalse);
1786         }
1787       sp++;
1788     }
1789 
1790     if (i%2 == 0)
1791       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1792 
1793     else
1794       (*dp++)+=unhex[(int) *sp++];
1795   }
1796   /*
1797     We have already read "Raw profile type.
1798   */
1799   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1800   profile=DestroyStringInfo(profile);
1801 
1802   if (image_info->verbose)
1803     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1804 
1805   return MagickTrue;
1806 }
1807 
1808 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
read_vpag_chunk_callback(png_struct * ping,png_unknown_chunkp chunk)1809 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1810 {
1811   Image
1812     *image;
1813 
1814 
1815   /* The unknown chunk structure contains the chunk data:
1816      png_byte name[5];
1817      png_byte *data;
1818      png_size_t size;
1819 
1820      Note that libpng has already taken care of the CRC handling.
1821   */
1822 
1823   LogMagickEvent(CoderEvent,GetMagickModule(),
1824      " read_vpag_chunk: found %c%c%c%c chunk",
1825        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1826 
1827   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1828       chunk->name[2] != 65 ||chunk-> name[3] != 103)
1829     return(0); /* Did not recognize */
1830 
1831   /* recognized vpAg */
1832 
1833   if (chunk->size != 9)
1834     return(-1); /* Error return */
1835 
1836   if (chunk->data[8] != 0)
1837     return(0);  /* ImageMagick requires pixel units */
1838 
1839   image=(Image *) png_get_user_chunk_ptr(ping);
1840 
1841   image->page.width=(size_t) ((chunk->data[0] << 24) |
1842      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1843 
1844   image->page.height=(size_t) ((chunk->data[4] << 24) |
1845      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1846 
1847   /* Return one of the following: */
1848      /* return(-n);  chunk had an error */
1849      /* return(0);  did not recognize */
1850      /* return(n);  success */
1851 
1852   return(1);
1853 
1854 }
1855 #endif
1856 
1857 #if defined(PNG_tIME_SUPPORTED)
read_tIME_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)1858 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1859   ExceptionInfo *exception)
1860 {
1861   png_timep
1862     time;
1863 
1864   if (png_get_tIME(ping,info,&time))
1865     {
1866       char
1867         timestamp[21];
1868 
1869       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1870         time->year,time->month,time->day,time->hour,time->minute,time->second);
1871       SetImageProperty(image,"png:tIME",timestamp,exception);
1872     }
1873 }
1874 #endif
1875 
1876 /*
1877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1878 %                                                                             %
1879 %                                                                             %
1880 %                                                                             %
1881 %   R e a d O n e P N G I m a g e                                             %
1882 %                                                                             %
1883 %                                                                             %
1884 %                                                                             %
1885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1886 %
1887 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1888 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
1889 %  necessary for the new Image structure and returns a pointer to the new
1890 %  image.
1891 %
1892 %  The format of the ReadOnePNGImage method is:
1893 %
1894 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1895 %         ExceptionInfo *exception)
1896 %
1897 %  A description of each parameter follows:
1898 %
1899 %    o mng_info: Specifies a pointer to a MngInfo structure.
1900 %
1901 %    o image_info: the image info.
1902 %
1903 %    o exception: return any errors or warnings in this structure.
1904 %
1905 */
ReadOnePNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)1906 static Image *ReadOnePNGImage(MngInfo *mng_info,
1907     const ImageInfo *image_info, ExceptionInfo *exception)
1908 {
1909   /* Read one PNG image */
1910 
1911   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1912 
1913   Image
1914     *image;
1915 
1916   char
1917     im_vers[32],
1918     libpng_runv[32],
1919     libpng_vers[32],
1920     zlib_runv[32],
1921     zlib_vers[32];
1922 
1923   int
1924     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
1925     num_raw_profiles,
1926     num_text,
1927     num_text_total,
1928     num_passes,
1929     number_colors,
1930     pass,
1931     ping_bit_depth,
1932     ping_color_type,
1933     ping_file_depth,
1934     ping_interlace_method,
1935     ping_compression_method,
1936     ping_filter_method,
1937     ping_num_trans,
1938     unit_type;
1939 
1940   double
1941     file_gamma;
1942 
1943   MagickBooleanType
1944     logging,
1945     ping_found_cHRM,
1946     ping_found_gAMA,
1947     ping_found_iCCP,
1948     ping_found_sRGB,
1949     ping_found_sRGB_cHRM,
1950     ping_preserve_iCCP,
1951     status;
1952 
1953   MemoryInfo
1954     *volatile pixel_info;
1955 
1956   PixelInfo
1957     transparent_color;
1958 
1959   PNGErrorInfo
1960     error_info;
1961 
1962   png_bytep
1963      ping_trans_alpha;
1964 
1965   png_color_16p
1966      ping_background,
1967      ping_trans_color;
1968 
1969   png_info
1970     *end_info,
1971     *ping_info;
1972 
1973   png_struct
1974     *ping;
1975 
1976   png_textp
1977     text;
1978 
1979   png_uint_32
1980     ping_height,
1981     ping_width,
1982     x_resolution,
1983     y_resolution;
1984 
1985   QuantumInfo
1986     *quantum_info;
1987 
1988   ssize_t
1989     ping_rowbytes,
1990     y;
1991 
1992   register unsigned char
1993     *p;
1994 
1995   register ssize_t
1996     i,
1997     x;
1998 
1999   register Quantum
2000     *q;
2001 
2002   size_t
2003     length,
2004     row_offset;
2005 
2006   ssize_t
2007     j;
2008 
2009   unsigned char
2010     *ping_pixels;
2011 
2012 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2013   png_byte unused_chunks[]=
2014   {
2015     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2016     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2017     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2018     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2019     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2020 #if !defined(PNG_tIME_SUPPORTED)
2021     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2022 #endif
2023 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2024                           /* ignore the APNG chunks */
2025      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2026     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2027     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2028 #endif
2029   };
2030 #endif
2031 
2032   /* Define these outside of the following "if logging()" block so they will
2033    * show in debuggers.
2034    */
2035   *im_vers='\0';
2036   (void) ConcatenateMagickString(im_vers,
2037          MagickLibVersionText,32);
2038   (void) ConcatenateMagickString(im_vers,
2039          MagickLibAddendum,32);
2040 
2041   *libpng_vers='\0';
2042   (void) ConcatenateMagickString(libpng_vers,
2043          PNG_LIBPNG_VER_STRING,32);
2044   *libpng_runv='\0';
2045   (void) ConcatenateMagickString(libpng_runv,
2046          png_get_libpng_ver(NULL),32);
2047 
2048   *zlib_vers='\0';
2049   (void) ConcatenateMagickString(zlib_vers,
2050          ZLIB_VERSION,32);
2051   *zlib_runv='\0';
2052   (void) ConcatenateMagickString(zlib_runv,
2053          zlib_version,32);
2054 
2055   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2056        "  Enter ReadOnePNGImage()\n"
2057        "    IM version     = %s\n"
2058        "    Libpng version = %s",
2059        im_vers, libpng_vers);
2060 
2061   if (logging != MagickFalse)
2062   {
2063     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2064     {
2065     LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2066         libpng_runv);
2067     }
2068     LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
2069         zlib_vers);
2070     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2071     {
2072     LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2073         zlib_runv);
2074     }
2075   }
2076 
2077 #if (PNG_LIBPNG_VER < 10200)
2078   if (image_info->verbose)
2079     printf("Your PNG library (libpng-%s) is rather old.\n",
2080        PNG_LIBPNG_VER_STRING);
2081 #endif
2082 
2083 #if (PNG_LIBPNG_VER >= 10400)
2084 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2085   if (image_info->verbose)
2086     {
2087       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2088            PNG_LIBPNG_VER_STRING);
2089       printf("Please update it.\n");
2090     }
2091 #  endif
2092 #endif
2093 
2094 
2095   quantum_info = (QuantumInfo *) NULL;
2096   image=mng_info->image;
2097 
2098   if (logging != MagickFalse)
2099   {
2100     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2101        "    Before reading:\n"
2102        "      image->alpha_trait=%d"
2103        "      image->rendering_intent=%d\n"
2104        "      image->colorspace=%d\n"
2105        "      image->gamma=%f",
2106        (int) image->alpha_trait, (int) image->rendering_intent,
2107        (int) image->colorspace, image->gamma);
2108   }
2109   intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2110 
2111   /* Set to an out-of-range color unless tRNS chunk is present */
2112   transparent_color.red=65537;
2113   transparent_color.green=65537;
2114   transparent_color.blue=65537;
2115   transparent_color.alpha=65537;
2116 
2117   number_colors=0;
2118   num_text = 0;
2119   num_text_total = 0;
2120   num_raw_profiles = 0;
2121 
2122   ping_found_cHRM = MagickFalse;
2123   ping_found_gAMA = MagickFalse;
2124   ping_found_iCCP = MagickFalse;
2125   ping_found_sRGB = MagickFalse;
2126   ping_found_sRGB_cHRM = MagickFalse;
2127   ping_preserve_iCCP = MagickFalse;
2128 
2129 
2130   /*
2131     Allocate the PNG structures
2132   */
2133 #ifdef PNG_USER_MEM_SUPPORTED
2134  error_info.image=image;
2135  error_info.exception=exception;
2136  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2137    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2138    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2139 #else
2140   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2141     MagickPNGErrorHandler,MagickPNGWarningHandler);
2142 #endif
2143   if (ping == (png_struct *) NULL)
2144     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2145 
2146   ping_info=png_create_info_struct(ping);
2147 
2148   if (ping_info == (png_info *) NULL)
2149     {
2150       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2151       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2152     }
2153 
2154   end_info=png_create_info_struct(ping);
2155 
2156   if (end_info == (png_info *) NULL)
2157     {
2158       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2159       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2160     }
2161 
2162   pixel_info=(MemoryInfo *) NULL;
2163 
2164   if (setjmp(png_jmpbuf(ping)))
2165     {
2166       /*
2167         PNG image is corrupt.
2168       */
2169       png_destroy_read_struct(&ping,&ping_info,&end_info);
2170 
2171 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2172       UnlockSemaphoreInfo(ping_semaphore);
2173 #endif
2174 
2175       if (pixel_info != (MemoryInfo *) NULL)
2176         pixel_info=RelinquishVirtualMemory(pixel_info);
2177 
2178       if (logging != MagickFalse)
2179         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180           "  exit ReadOnePNGImage() with error.");
2181 
2182       if (image != (Image *) NULL)
2183         {
2184           const char
2185             *option;
2186 
2187           option=GetImageOption(image_info,"png:preserve-corrupt-image");
2188           if (IsStringTrue(option) == MagickFalse)
2189             image->columns=0;
2190         }
2191 
2192       return(GetFirstImageInList(image));
2193     }
2194 
2195   /* {  For navigation to end of SETJMP-protected block.  Within this
2196    *    block, use png_error() instead of Throwing an Exception, to ensure
2197    *    that libpng is able to clean up, and that the semaphore is unlocked.
2198    */
2199 
2200 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2201   LockSemaphoreInfo(ping_semaphore);
2202 #endif
2203 
2204 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2205   /* Allow benign errors */
2206   png_set_benign_errors(ping, 1);
2207 #endif
2208 
2209 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2210   /* Reject images with too many rows or columns */
2211   png_set_user_limits(ping,
2212     (png_uint_32) MagickMin(0x7fffffffL,
2213         GetMagickResourceLimit(WidthResource)),
2214     (png_uint_32) MagickMin(0x7fffffffL,
2215         GetMagickResourceLimit(HeightResource)));
2216 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2217 
2218   /*
2219     Prepare PNG for reading.
2220   */
2221 
2222   mng_info->image_found++;
2223   png_set_sig_bytes(ping,8);
2224 
2225   if (LocaleCompare(image_info->magick,"MNG") == 0)
2226     {
2227 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2228       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2229       png_set_read_fn(ping,image,png_get_data);
2230 #else
2231 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2232       png_permit_empty_plte(ping,MagickTrue);
2233       png_set_read_fn(ping,image,png_get_data);
2234 #else
2235       mng_info->image=image;
2236       mng_info->bytes_in_read_buffer=0;
2237       mng_info->found_empty_plte=MagickFalse;
2238       mng_info->have_saved_bkgd_index=MagickFalse;
2239       png_set_read_fn(ping,mng_info,mng_get_data);
2240 #endif
2241 #endif
2242     }
2243 
2244   else
2245     png_set_read_fn(ping,image,png_get_data);
2246 
2247   {
2248     const char
2249       *value;
2250 
2251     value=GetImageOption(image_info,"profile:skip");
2252 
2253     if (IsOptionMember("ICC",value) == MagickFalse)
2254     {
2255 
2256        value=GetImageOption(image_info,"png:preserve-iCCP");
2257 
2258        if (value == NULL)
2259           value=GetImageArtifact(image,"png:preserve-iCCP");
2260 
2261        if (value != NULL)
2262           ping_preserve_iCCP=MagickTrue;
2263 
2264 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2265        /* Don't let libpng check for ICC/sRGB profile because we're going
2266         * to do that anyway.  This feature was added at libpng-1.6.12.
2267         * If logging, go ahead and check and issue a warning as appropriate.
2268         */
2269        if (logging == MagickFalse)
2270           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2271 #endif
2272     }
2273 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2274     else
2275     {
2276        png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2277     }
2278 #endif
2279   }
2280 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2281   /* Ignore unused chunks and all unknown chunks except for vpAg */
2282 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2283   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2284 #else
2285   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2286 #endif
2287   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2288   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2289      (int)sizeof(unused_chunks)/5);
2290   /* Callback for other unknown chunks */
2291   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2292 #endif
2293 
2294 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2295 #  if (PNG_LIBPNG_VER >= 10400)
2296     /* Limit the size of the chunk storage cache used for sPLT, text,
2297      * and unknown chunks.
2298      */
2299     png_set_chunk_cache_max(ping, 32767);
2300 #  endif
2301 #endif
2302 
2303 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2304     /* Disable new libpng-1.5.10 feature */
2305     png_set_check_for_invalid_index (ping, 0);
2306 #endif
2307 
2308 #if (PNG_LIBPNG_VER < 10400)
2309 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2310    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2311   /* Disable thread-unsafe features of pnggccrd */
2312   if (png_access_version_number() >= 10200)
2313   {
2314     png_uint_32 mmx_disable_mask=0;
2315     png_uint_32 asm_flags;
2316 
2317     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2318                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2319                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2320                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2321     asm_flags=png_get_asm_flags(ping);
2322     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2323   }
2324 #  endif
2325 #endif
2326 
2327   png_read_info(ping,ping_info);
2328 
2329   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2330                &ping_bit_depth,&ping_color_type,
2331                &ping_interlace_method,&ping_compression_method,
2332                &ping_filter_method);
2333 
2334   ping_file_depth = ping_bit_depth;
2335 
2336   /* Swap bytes if requested */
2337   if (ping_file_depth == 16)
2338   {
2339      const char
2340        *value;
2341 
2342      value=GetImageOption(image_info,"png:swap-bytes");
2343 
2344      if (value == NULL)
2345         value=GetImageArtifact(image,"png:swap-bytes");
2346 
2347      if (value != NULL)
2348         png_set_swap(ping);
2349   }
2350 
2351   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2352   {
2353       char
2354         msg[MagickPathExtent];
2355 
2356       (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_color_type);
2357       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2358 
2359       (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_bit_depth);
2360       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2361   }
2362 
2363   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2364                       &ping_trans_color);
2365 
2366   (void) png_get_bKGD(ping, ping_info, &ping_background);
2367 
2368   if (ping_bit_depth < 8)
2369     {
2370        png_set_packing(ping);
2371        ping_bit_depth = 8;
2372     }
2373 
2374   image->depth=ping_bit_depth;
2375   image->depth=GetImageQuantumDepth(image,MagickFalse);
2376   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2377 
2378   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2379       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2380     {
2381       image->rendering_intent=UndefinedIntent;
2382       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2383       (void) ResetMagickMemory(&image->chromaticity,0,
2384         sizeof(image->chromaticity));
2385     }
2386 
2387   if (logging != MagickFalse)
2388     {
2389       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2390         "    PNG width: %.20g, height: %.20g\n"
2391         "    PNG color_type: %d, bit_depth: %d\n"
2392         "    PNG compression_method: %d\n"
2393         "    PNG interlace_method: %d, filter_method: %d",
2394         (double) ping_width, (double) ping_height,
2395         ping_color_type, ping_bit_depth,
2396         ping_compression_method,
2397         ping_interlace_method,ping_filter_method);
2398 
2399     }
2400 
2401   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2402     {
2403       ping_found_iCCP=MagickTrue;
2404       if (logging != MagickFalse)
2405         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2406           "    Found PNG iCCP chunk.");
2407     }
2408 
2409   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2410     {
2411       ping_found_gAMA=MagickTrue;
2412       if (logging != MagickFalse)
2413         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2414           "    Found PNG gAMA chunk.");
2415     }
2416 
2417   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2418     {
2419       ping_found_cHRM=MagickTrue;
2420       if (logging != MagickFalse)
2421         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2422           "    Found PNG cHRM chunk.");
2423     }
2424 
2425   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2426       PNG_INFO_sRGB))
2427     {
2428       ping_found_sRGB=MagickTrue;
2429       if (logging != MagickFalse)
2430         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2431           "    Found PNG sRGB chunk.");
2432     }
2433 
2434 #ifdef PNG_READ_iCCP_SUPPORTED
2435     if (ping_found_iCCP !=MagickTrue &&
2436       ping_found_sRGB != MagickTrue &&
2437       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2438     {
2439       ping_found_iCCP=MagickTrue;
2440       if (logging != MagickFalse)
2441         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2442           "    Found PNG iCCP chunk.");
2443     }
2444 
2445   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2446     {
2447       int
2448         compression;
2449 
2450 #if (PNG_LIBPNG_VER < 10500)
2451       png_charp
2452         info;
2453 #else
2454       png_bytep
2455         info;
2456 #endif
2457 
2458       png_charp
2459         name;
2460 
2461       png_uint_32
2462         profile_length;
2463 
2464       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2465         &profile_length);
2466 
2467       if (profile_length != 0)
2468         {
2469           StringInfo
2470             *profile;
2471 
2472           if (logging != MagickFalse)
2473             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2474               "    Reading PNG iCCP chunk.");
2475 
2476           profile=BlobToStringInfo(info,profile_length);
2477 
2478           if (profile == (StringInfo *) NULL)
2479           {
2480             png_warning(ping, "ICC profile is NULL");
2481             profile=DestroyStringInfo(profile);
2482           }
2483           else
2484           {
2485             if (ping_preserve_iCCP == MagickFalse)
2486             {
2487                  int
2488                    icheck,
2489                    got_crc=0;
2490 
2491 
2492                  png_uint_32
2493                    length,
2494                    profile_crc=0;
2495 
2496                  unsigned char
2497                    *data;
2498 
2499                  length=(png_uint_32) GetStringInfoLength(profile);
2500 
2501                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2502                  {
2503                    if (length == sRGB_info[icheck].len)
2504                    {
2505                      if (got_crc == 0)
2506                      {
2507                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2508                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2509                          (unsigned long) length);
2510 
2511                        data=GetStringInfoDatum(profile);
2512                        profile_crc=crc32(0,data,length);
2513 
2514                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2515                            "      with crc=%8x",(unsigned int) profile_crc);
2516                        got_crc++;
2517                      }
2518 
2519                      if (profile_crc == sRGB_info[icheck].crc)
2520                      {
2521                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2522                             "      It is sRGB with rendering intent = %s",
2523                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2524                              sRGB_info[icheck].intent));
2525                         if (image->rendering_intent==UndefinedIntent)
2526                         {
2527                           image->rendering_intent=
2528                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2529                              sRGB_info[icheck].intent);
2530                         }
2531                         break;
2532                      }
2533                    }
2534                  }
2535                  if (sRGB_info[icheck].len == 0)
2536                  {
2537                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538                         "    Got a %lu-byte ICC profile not recognized as sRGB",
2539                         (unsigned long) length);
2540                     (void) SetImageProfile(image,"icc",profile,exception);
2541                  }
2542             }
2543             else /* Preserve-iCCP */
2544             {
2545                     (void) SetImageProfile(image,"icc",profile,exception);
2546             }
2547 
2548             profile=DestroyStringInfo(profile);
2549           }
2550       }
2551     }
2552 #endif
2553 
2554 #if defined(PNG_READ_sRGB_SUPPORTED)
2555   {
2556     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2557         PNG_INFO_sRGB))
2558     {
2559       if (png_get_sRGB(ping,ping_info,&intent))
2560       {
2561         if (image->rendering_intent == UndefinedIntent)
2562           image->rendering_intent=
2563              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2564 
2565         if (logging != MagickFalse)
2566           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2567             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2568       }
2569     }
2570 
2571     else if (mng_info->have_global_srgb)
2572       {
2573         if (image->rendering_intent == UndefinedIntent)
2574           image->rendering_intent=
2575             Magick_RenderingIntent_from_PNG_RenderingIntent
2576             (mng_info->global_srgb_intent);
2577       }
2578   }
2579 #endif
2580 
2581 
2582   {
2583      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2584        if (mng_info->have_global_gama)
2585          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2586 
2587      if (png_get_gAMA(ping,ping_info,&file_gamma))
2588        {
2589          image->gamma=(float) file_gamma;
2590          if (logging != MagickFalse)
2591            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2592              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2593        }
2594   }
2595 
2596   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2597     {
2598       if (mng_info->have_global_chrm != MagickFalse)
2599         {
2600           (void) png_set_cHRM(ping,ping_info,
2601             mng_info->global_chrm.white_point.x,
2602             mng_info->global_chrm.white_point.y,
2603             mng_info->global_chrm.red_primary.x,
2604             mng_info->global_chrm.red_primary.y,
2605             mng_info->global_chrm.green_primary.x,
2606             mng_info->global_chrm.green_primary.y,
2607             mng_info->global_chrm.blue_primary.x,
2608             mng_info->global_chrm.blue_primary.y);
2609         }
2610     }
2611 
2612   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2613     {
2614       (void) png_get_cHRM(ping,ping_info,
2615         &image->chromaticity.white_point.x,
2616         &image->chromaticity.white_point.y,
2617         &image->chromaticity.red_primary.x,
2618         &image->chromaticity.red_primary.y,
2619         &image->chromaticity.green_primary.x,
2620         &image->chromaticity.green_primary.y,
2621         &image->chromaticity.blue_primary.x,
2622         &image->chromaticity.blue_primary.y);
2623 
2624        ping_found_cHRM=MagickTrue;
2625 
2626        if (image->chromaticity.red_primary.x>0.6399f &&
2627            image->chromaticity.red_primary.x<0.6401f &&
2628            image->chromaticity.red_primary.y>0.3299f &&
2629            image->chromaticity.red_primary.y<0.3301f &&
2630            image->chromaticity.green_primary.x>0.2999f &&
2631            image->chromaticity.green_primary.x<0.3001f &&
2632            image->chromaticity.green_primary.y>0.5999f &&
2633            image->chromaticity.green_primary.y<0.6001f &&
2634            image->chromaticity.blue_primary.x>0.1499f &&
2635            image->chromaticity.blue_primary.x<0.1501f &&
2636            image->chromaticity.blue_primary.y>0.0599f &&
2637            image->chromaticity.blue_primary.y<0.0601f &&
2638            image->chromaticity.white_point.x>0.3126f &&
2639            image->chromaticity.white_point.x<0.3128f &&
2640            image->chromaticity.white_point.y>0.3289f &&
2641            image->chromaticity.white_point.y<0.3291f)
2642           ping_found_sRGB_cHRM=MagickTrue;
2643     }
2644 
2645   if (image->rendering_intent != UndefinedIntent)
2646     {
2647       if (ping_found_sRGB != MagickTrue &&
2648           (ping_found_gAMA != MagickTrue ||
2649           (image->gamma > .45 && image->gamma < .46)) &&
2650           (ping_found_cHRM != MagickTrue ||
2651           ping_found_sRGB_cHRM != MagickFalse) &&
2652           ping_found_iCCP != MagickTrue)
2653       {
2654          png_set_sRGB(ping,ping_info,
2655             Magick_RenderingIntent_to_PNG_RenderingIntent
2656             (image->rendering_intent));
2657          file_gamma=1.000f/2.200f;
2658          ping_found_sRGB=MagickTrue;
2659          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2660            "    Setting sRGB as if in input");
2661       }
2662     }
2663 
2664 #if defined(PNG_oFFs_SUPPORTED)
2665   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2666     {
2667       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2668       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2669 
2670       if (logging != MagickFalse)
2671         if (image->page.x || image->page.y)
2672           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2673             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2674             image->page.x,(double) image->page.y);
2675     }
2676 #endif
2677 #if defined(PNG_pHYs_SUPPORTED)
2678   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2679     {
2680       if (mng_info->have_global_phys)
2681         {
2682           png_set_pHYs(ping,ping_info,
2683                        mng_info->global_x_pixels_per_unit,
2684                        mng_info->global_y_pixels_per_unit,
2685                        mng_info->global_phys_unit_type);
2686         }
2687     }
2688 
2689   x_resolution=0;
2690   y_resolution=0;
2691   unit_type=0;
2692   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2693     {
2694       /*
2695         Set image resolution.
2696       */
2697       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2698         &unit_type);
2699       image->resolution.x=(double) x_resolution;
2700       image->resolution.y=(double) y_resolution;
2701 
2702       if (unit_type == PNG_RESOLUTION_METER)
2703         {
2704           image->units=PixelsPerCentimeterResolution;
2705           image->resolution.x=(double) x_resolution/100.0;
2706           image->resolution.y=(double) y_resolution/100.0;
2707         }
2708 
2709       if (logging != MagickFalse)
2710         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2712           (double) x_resolution,(double) y_resolution,unit_type);
2713     }
2714 #endif
2715 
2716   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2717     {
2718       png_colorp
2719         palette;
2720 
2721       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2722 
2723       if ((number_colors == 0) &&
2724           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2725         {
2726           if (mng_info->global_plte_length)
2727             {
2728               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2729                 (int) mng_info->global_plte_length);
2730 
2731               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2732               {
2733                 if (mng_info->global_trns_length)
2734                   {
2735                     png_warning(ping,
2736                       "global tRNS has more entries than global PLTE");
2737                   }
2738                 else
2739                   {
2740                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2741                        (int) mng_info->global_trns_length,NULL);
2742                   }
2743                }
2744 #ifdef PNG_READ_bKGD_SUPPORTED
2745               if (
2746 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2747                    mng_info->have_saved_bkgd_index ||
2748 #endif
2749                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2750                     {
2751                       png_color_16
2752                          background;
2753 
2754 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2755                       if (mng_info->have_saved_bkgd_index)
2756                         background.index=mng_info->saved_bkgd_index;
2757 #endif
2758                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2759                         background.index=ping_background->index;
2760 
2761                       background.red=(png_uint_16)
2762                         mng_info->global_plte[background.index].red;
2763 
2764                       background.green=(png_uint_16)
2765                         mng_info->global_plte[background.index].green;
2766 
2767                       background.blue=(png_uint_16)
2768                         mng_info->global_plte[background.index].blue;
2769 
2770                       background.gray=(png_uint_16)
2771                         mng_info->global_plte[background.index].green;
2772 
2773                       png_set_bKGD(ping,ping_info,&background);
2774                     }
2775 #endif
2776                 }
2777               else
2778                 png_error(ping,"No global PLTE in file");
2779             }
2780         }
2781 
2782 #ifdef PNG_READ_bKGD_SUPPORTED
2783   if (mng_info->have_global_bkgd &&
2784           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2785       image->background_color=mng_info->mng_global_bkgd;
2786 
2787   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2788     {
2789       unsigned int
2790         bkgd_scale;
2791 
2792       /* Set image background color.
2793        * Scale background components to 16-bit, then scale
2794        * to quantum depth
2795        */
2796 
2797         bkgd_scale = 1;
2798 
2799         if (ping_file_depth == 1)
2800            bkgd_scale = 255;
2801 
2802         else if (ping_file_depth == 2)
2803            bkgd_scale = 85;
2804 
2805         else if (ping_file_depth == 4)
2806            bkgd_scale = 17;
2807 
2808         if (ping_file_depth <= 8)
2809            bkgd_scale *= 257;
2810 
2811         ping_background->red *= bkgd_scale;
2812         ping_background->green *= bkgd_scale;
2813         ping_background->blue *= bkgd_scale;
2814 
2815         if (logging != MagickFalse)
2816           {
2817             if (logging != MagickFalse)
2818               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2819                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2820                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d).",
2821                  ping_background->red,ping_background->green,
2822                  ping_background->blue,
2823                  bkgd_scale,ping_background->red,
2824                  ping_background->green,ping_background->blue);
2825           }
2826 
2827         image->background_color.red=
2828             ScaleShortToQuantum(ping_background->red);
2829 
2830         image->background_color.green=
2831             ScaleShortToQuantum(ping_background->green);
2832 
2833         image->background_color.blue=
2834           ScaleShortToQuantum(ping_background->blue);
2835 
2836         image->background_color.alpha=OpaqueAlpha;
2837 
2838         if (logging != MagickFalse)
2839           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2840             "    image->background_color=(%.20g,%.20g,%.20g).",
2841             (double) image->background_color.red,
2842             (double) image->background_color.green,
2843             (double) image->background_color.blue);
2844     }
2845 #endif /* PNG_READ_bKGD_SUPPORTED */
2846 
2847   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2848     {
2849       /*
2850         Image has a tRNS chunk.
2851       */
2852       int
2853         max_sample;
2854 
2855       size_t
2856         one=1;
2857 
2858       if (logging != MagickFalse)
2859         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2860           "    Reading PNG tRNS chunk.");
2861 
2862       max_sample = (int) ((one << ping_file_depth) - 1);
2863 
2864       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2865           (int)ping_trans_color->gray > max_sample) ||
2866           (ping_color_type == PNG_COLOR_TYPE_RGB &&
2867           ((int)ping_trans_color->red > max_sample ||
2868           (int)ping_trans_color->green > max_sample ||
2869           (int)ping_trans_color->blue > max_sample)))
2870         {
2871           if (logging != MagickFalse)
2872             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2873               "    Ignoring PNG tRNS chunk with out-of-range sample.");
2874           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2875           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2876           image->alpha_trait=UndefinedPixelTrait;
2877         }
2878       else
2879         {
2880           int
2881             scale_to_short;
2882 
2883           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2884 
2885           /* Scale transparent_color to short */
2886           transparent_color.red= scale_to_short*ping_trans_color->red;
2887           transparent_color.green= scale_to_short*ping_trans_color->green;
2888           transparent_color.blue= scale_to_short*ping_trans_color->blue;
2889           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2890 
2891           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2892             {
2893               if (logging != MagickFalse)
2894               {
2895                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2896                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
2897                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
2898 
2899               }
2900               transparent_color.red=transparent_color.alpha;
2901               transparent_color.green=transparent_color.alpha;
2902               transparent_color.blue=transparent_color.alpha;
2903             }
2904         }
2905     }
2906 #if defined(PNG_READ_sBIT_SUPPORTED)
2907   if (mng_info->have_global_sbit)
2908     {
2909       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2910         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2911     }
2912 #endif
2913   num_passes=png_set_interlace_handling(ping);
2914 
2915   png_read_update_info(ping,ping_info);
2916 
2917   ping_rowbytes=png_get_rowbytes(ping,ping_info);
2918 
2919   /*
2920     Initialize image structure.
2921   */
2922   mng_info->image_box.left=0;
2923   mng_info->image_box.right=(ssize_t) ping_width;
2924   mng_info->image_box.top=0;
2925   mng_info->image_box.bottom=(ssize_t) ping_height;
2926   if (mng_info->mng_type == 0)
2927     {
2928       mng_info->mng_width=ping_width;
2929       mng_info->mng_height=ping_height;
2930       mng_info->frame=mng_info->image_box;
2931       mng_info->clip=mng_info->image_box;
2932     }
2933 
2934   else
2935     {
2936       image->page.y=mng_info->y_off[mng_info->object_id];
2937     }
2938 
2939   image->compression=ZipCompression;
2940   image->columns=ping_width;
2941   image->rows=ping_height;
2942 
2943   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2944       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2945     {
2946       double
2947         image_gamma = image->gamma;
2948 
2949       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2950          "    image->gamma=%f",(float) image_gamma);
2951 
2952       if (image_gamma > 0.75)
2953         {
2954           /* Set image->rendering_intent to Undefined,
2955            * image->colorspace to GRAY, and reset image->chromaticity.
2956            */
2957           image->intensity = Rec709LuminancePixelIntensityMethod;
2958           SetImageColorspace(image,GRAYColorspace,exception);
2959         }
2960       else
2961         {
2962           RenderingIntent
2963             save_rendering_intent = image->rendering_intent;
2964           ChromaticityInfo
2965             save_chromaticity = image->chromaticity;
2966 
2967           SetImageColorspace(image,GRAYColorspace,exception);
2968           image->rendering_intent = save_rendering_intent;
2969           image->chromaticity = save_chromaticity;
2970         }
2971 
2972       image->gamma = image_gamma;
2973     }
2974 
2975   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2976       "    image->colorspace=%d",(int) image->colorspace);
2977 
2978   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2979       ((int) ping_bit_depth < 16 &&
2980       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2981     {
2982       size_t
2983         one;
2984 
2985       image->storage_class=PseudoClass;
2986       one=1;
2987       image->colors=one << ping_file_depth;
2988 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2989       if (image->colors > 256)
2990         image->colors=256;
2991 #else
2992       if (image->colors > 65536L)
2993         image->colors=65536L;
2994 #endif
2995       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2996         {
2997           png_colorp
2998             palette;
2999 
3000           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3001           image->colors=(size_t) number_colors;
3002 
3003           if (logging != MagickFalse)
3004             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3005               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3006         }
3007     }
3008 
3009   if (image->storage_class == PseudoClass)
3010     {
3011       /*
3012         Initialize image colormap.
3013       */
3014       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3015         png_error(ping,"Memory allocation failed");
3016 
3017       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3018         {
3019           png_colorp
3020             palette;
3021 
3022           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3023 
3024           for (i=0; i < (ssize_t) number_colors; i++)
3025           {
3026             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3027             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3028             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3029           }
3030 
3031           for ( ; i < (ssize_t) image->colors; i++)
3032           {
3033             image->colormap[i].red=0;
3034             image->colormap[i].green=0;
3035             image->colormap[i].blue=0;
3036           }
3037         }
3038 
3039       else
3040         {
3041           Quantum
3042             scale;
3043 
3044           scale = (Quantum) ((65535UL)/((1UL << ping_file_depth)-1));
3045 
3046 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3047           scale = ScaleShortToQuantum(scale);
3048 #endif
3049 
3050           for (i=0; i < (ssize_t) image->colors; i++)
3051           {
3052             image->colormap[i].red=(Quantum) (i*scale);
3053             image->colormap[i].green=(Quantum) (i*scale);
3054             image->colormap[i].blue=(Quantum) (i*scale);
3055           }
3056        }
3057     }
3058 
3059    /* Set some properties for reporting by "identify" */
3060     {
3061       char
3062         msg[MagickPathExtent];
3063 
3064      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3065         ping_interlace_method in value */
3066 
3067      (void) FormatLocaleString(msg,MagickPathExtent,
3068          "%d, %d",(int) ping_width, (int) ping_height);
3069      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3070 
3071      (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_file_depth);
3072      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3073 
3074      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3075          (int) ping_color_type,
3076          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3077      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3078 
3079      if (ping_interlace_method == 0)
3080        {
3081          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3082             (int) ping_interlace_method);
3083        }
3084      else if (ping_interlace_method == 1)
3085        {
3086          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3087             (int) ping_interlace_method);
3088        }
3089      else
3090        {
3091          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3092             (int) ping_interlace_method);
3093        }
3094        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3095 
3096      if (number_colors != 0)
3097        {
3098          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3099             (int) number_colors);
3100          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3101             exception);
3102        }
3103    }
3104 #if defined(PNG_tIME_SUPPORTED)
3105    read_tIME_chunk(image,ping,ping_info,exception);
3106 #endif
3107 
3108 
3109   /*
3110     Read image scanlines.
3111   */
3112   if (image->delay != 0)
3113     mng_info->scenes_found++;
3114 
3115   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3116       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3117       (image_info->first_scene+image_info->number_scenes))))
3118     {
3119       /* This happens later in non-ping decodes */
3120       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3121         image->storage_class=DirectClass;
3122       image->alpha_trait=
3123         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3124          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3125          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3126         BlendPixelTrait : UndefinedPixelTrait;
3127 
3128       if (logging != MagickFalse)
3129         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3130           "    Skipping PNG image data for scene %.20g",(double)
3131           mng_info->scenes_found-1);
3132       png_destroy_read_struct(&ping,&ping_info,&end_info);
3133 
3134 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3135       UnlockSemaphoreInfo(ping_semaphore);
3136 #endif
3137 
3138       if (logging != MagickFalse)
3139         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3140           "  exit ReadOnePNGImage().");
3141 
3142       return(image);
3143     }
3144 
3145   if (logging != MagickFalse)
3146     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3147       "    Reading PNG IDAT chunk(s)");
3148 
3149   status=SetImageExtent(image,image->columns,image->rows,exception);
3150   if (status == MagickFalse)
3151     return(DestroyImageList(image));
3152 
3153   if (num_passes > 1)
3154     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3155       sizeof(*ping_pixels));
3156   else
3157     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3158 
3159   if (pixel_info == (MemoryInfo *) NULL)
3160     png_error(ping,"Memory allocation failed");
3161   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3162 
3163   if (logging != MagickFalse)
3164     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3165       "    Converting PNG pixels to pixel packets");
3166   /*
3167     Convert PNG pixels to pixel packets.
3168   */
3169   quantum_info=AcquireQuantumInfo(image_info,image);
3170 
3171   if (quantum_info == (QuantumInfo *) NULL)
3172      png_error(ping,"Failed to allocate quantum_info");
3173 
3174   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3175 
3176   {
3177 
3178    MagickBooleanType
3179      found_transparent_pixel;
3180 
3181   found_transparent_pixel=MagickFalse;
3182 
3183   if (image->storage_class == DirectClass)
3184     {
3185       for (pass=0; pass < num_passes; pass++)
3186       {
3187         /*
3188           Convert image to DirectClass pixel packets.
3189         */
3190         image->alpha_trait=
3191             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3192             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3193             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3194             BlendPixelTrait : UndefinedPixelTrait;
3195 
3196         for (y=0; y < (ssize_t) image->rows; y++)
3197         {
3198           if (num_passes > 1)
3199             row_offset=ping_rowbytes*y;
3200 
3201           else
3202             row_offset=0;
3203 
3204           png_read_row(ping,ping_pixels+row_offset,NULL);
3205 
3206           if (pass < num_passes-1)
3207             continue;
3208 
3209           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3210 
3211           if (q == (Quantum *) NULL)
3212             break;
3213 
3214           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3215             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3216               GrayQuantum,ping_pixels+row_offset,exception);
3217 
3218           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3219             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3220               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3221 
3222           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3223             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3224               RGBAQuantum,ping_pixels+row_offset,exception);
3225 
3226           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3227             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3228               IndexQuantum,ping_pixels+row_offset,exception);
3229 
3230           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3231             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3232               RGBQuantum,ping_pixels+row_offset,exception);
3233 
3234           if (found_transparent_pixel == MagickFalse)
3235             {
3236               /* Is there a transparent pixel in the row? */
3237               if (y== 0 && logging != MagickFalse)
3238                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3239                    "    Looking for cheap transparent pixel");
3240 
3241               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3242               {
3243                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3244                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3245                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3246                   {
3247                     if (logging != MagickFalse)
3248                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3249                         "    ...got one.");
3250 
3251                     found_transparent_pixel = MagickTrue;
3252                     break;
3253                   }
3254                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3255                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3256                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3257                     transparent_color.red &&
3258                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3259                     transparent_color.green &&
3260                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3261                     transparent_color.blue))
3262                   {
3263                     if (logging != MagickFalse)
3264                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3265                         "    ...got one.");
3266                     found_transparent_pixel = MagickTrue;
3267                     break;
3268                   }
3269                 q+=GetPixelChannels(image);
3270               }
3271             }
3272 
3273           if (num_passes == 1)
3274             {
3275               status=SetImageProgress(image,LoadImageTag,
3276                   (MagickOffsetType) y, image->rows);
3277 
3278               if (status == MagickFalse)
3279                 break;
3280             }
3281           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3282             break;
3283         }
3284 
3285         if (num_passes != 1)
3286           {
3287             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3288             if (status == MagickFalse)
3289               break;
3290           }
3291       }
3292     }
3293 
3294   else /* image->storage_class != DirectClass */
3295 
3296     for (pass=0; pass < num_passes; pass++)
3297     {
3298       Quantum
3299         *quantum_scanline;
3300 
3301       register Quantum
3302         *r;
3303 
3304       /*
3305         Convert grayscale image to PseudoClass pixel packets.
3306       */
3307       if (logging != MagickFalse)
3308         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3309           "    Converting grayscale pixels to pixel packets");
3310 
3311       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3312         BlendPixelTrait : UndefinedPixelTrait;
3313 
3314       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3315         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3316         sizeof(*quantum_scanline));
3317 
3318       if (quantum_scanline == (Quantum *) NULL)
3319         png_error(ping,"Memory allocation failed");
3320 
3321       for (y=0; y < (ssize_t) image->rows; y++)
3322       {
3323         Quantum
3324            alpha;
3325 
3326         if (num_passes > 1)
3327           row_offset=ping_rowbytes*y;
3328 
3329         else
3330           row_offset=0;
3331 
3332         png_read_row(ping,ping_pixels+row_offset,NULL);
3333 
3334         if (pass < num_passes-1)
3335           continue;
3336 
3337         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3338 
3339         if (q == (Quantum *) NULL)
3340           break;
3341 
3342         p=ping_pixels+row_offset;
3343         r=quantum_scanline;
3344 
3345         switch (ping_bit_depth)
3346         {
3347           case 8:
3348           {
3349 
3350             if (ping_color_type == 4)
3351               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3352               {
3353                 *r++=*p++;
3354 
3355                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3356 
3357                 SetPixelAlpha(image,alpha,q);
3358 
3359                 if (alpha != OpaqueAlpha)
3360                   found_transparent_pixel = MagickTrue;
3361 
3362                 q+=GetPixelChannels(image);
3363               }
3364 
3365             else
3366               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3367                 *r++=*p++;
3368 
3369             break;
3370           }
3371 
3372           case 16:
3373           {
3374             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3375             {
3376 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3377               unsigned short
3378                 quantum;
3379 
3380               if (image->colors > 256)
3381                 quantum=((*p++) << 8);
3382 
3383               else
3384                 quantum=0;
3385 
3386               quantum|=(*p++);
3387               *r=ScaleShortToQuantum(quantum);
3388               r++;
3389 
3390               if (ping_color_type == 4)
3391                 {
3392                   if (image->colors > 256)
3393                     quantum=((*p++) << 8);
3394                   else
3395                     quantum=0;
3396 
3397                   quantum|=(*p++);
3398 
3399                   alpha=ScaleShortToQuantum(quantum);
3400                   SetPixelAlpha(image,alpha,q);
3401 
3402                   if (alpha != OpaqueAlpha)
3403                     found_transparent_pixel = MagickTrue;
3404 
3405                   q+=GetPixelChannels(image);
3406                 }
3407 
3408 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3409               *r++=(*p++);
3410               p++; /* strip low byte */
3411 
3412               if (ping_color_type == 4)
3413                 {
3414                   SetPixelAlpha(image,*p++,q);
3415 
3416                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3417                     found_transparent_pixel = MagickTrue;
3418 
3419                   p++;
3420                   q+=GetPixelChannels(image);
3421                 }
3422 #endif
3423             }
3424 
3425             break;
3426           }
3427 
3428           default:
3429             break;
3430         }
3431 
3432         /*
3433           Transfer image scanline.
3434         */
3435         r=quantum_scanline;
3436 
3437         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3438 
3439         if (q == (Quantum *) NULL)
3440           break;
3441         for (x=0; x < (ssize_t) image->columns; x++)
3442         {
3443           SetPixelIndex(image,*r++,q);
3444           q+=GetPixelChannels(image);
3445         }
3446 
3447         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3448           break;
3449 
3450         if (num_passes == 1)
3451           {
3452             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3453               image->rows);
3454 
3455             if (status == MagickFalse)
3456               break;
3457           }
3458       }
3459 
3460       if (num_passes != 1)
3461         {
3462           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3463 
3464           if (status == MagickFalse)
3465             break;
3466         }
3467 
3468       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3469     }
3470 
3471     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3472       UndefinedPixelTrait;
3473 
3474     if (logging != MagickFalse)
3475       {
3476         if (found_transparent_pixel != MagickFalse)
3477           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3478             "    Found transparent pixel");
3479         else
3480           {
3481             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3482               "    No transparent pixel was found");
3483 
3484             ping_color_type&=0x03;
3485           }
3486       }
3487     }
3488 
3489   if (quantum_info != (QuantumInfo *) NULL)
3490     quantum_info=DestroyQuantumInfo(quantum_info);
3491 
3492   if (image->storage_class == PseudoClass)
3493     {
3494       PixelTrait
3495         alpha_trait;
3496 
3497       alpha_trait=image->alpha_trait;
3498       image->alpha_trait=UndefinedPixelTrait;
3499       (void) SyncImage(image,exception);
3500       image->alpha_trait=alpha_trait;
3501     }
3502 
3503   png_read_end(ping,end_info);
3504 
3505   if (logging != MagickFalse)
3506   {
3507     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3508        "  image->storage_class=%d\n",(int) image->storage_class);
3509   }
3510 
3511   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3512       (ssize_t) image_info->first_scene && image->delay != 0)
3513     {
3514       png_destroy_read_struct(&ping,&ping_info,&end_info);
3515       pixel_info=RelinquishVirtualMemory(pixel_info);
3516       image->colors=2;
3517       (void) SetImageBackgroundColor(image,exception);
3518 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3519       UnlockSemaphoreInfo(ping_semaphore);
3520 #endif
3521       if (logging != MagickFalse)
3522         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3523           "  exit ReadOnePNGImage() early.");
3524       return(image);
3525     }
3526 
3527   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3528     {
3529       ClassType
3530         storage_class;
3531 
3532       /*
3533         Image has a transparent background.
3534       */
3535       storage_class=image->storage_class;
3536       image->alpha_trait=BlendPixelTrait;
3537 
3538 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3539 
3540       if (storage_class == PseudoClass)
3541         {
3542           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3543             {
3544               for (x=0; x < ping_num_trans; x++)
3545               {
3546                  image->colormap[x].alpha_trait=BlendPixelTrait;
3547                  image->colormap[x].alpha =
3548                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3549               }
3550             }
3551 
3552           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3553             {
3554               for (x=0; x < (int) image->colors; x++)
3555               {
3556                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3557                      transparent_color.alpha)
3558                  {
3559                     image->colormap[x].alpha_trait=BlendPixelTrait;
3560                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3561                  }
3562               }
3563             }
3564           (void) SyncImage(image,exception);
3565         }
3566 
3567 #if 1 /* Should have already been done above, but glennrp problem P10
3568        * needs this.
3569        */
3570       else
3571         {
3572           for (y=0; y < (ssize_t) image->rows; y++)
3573           {
3574             image->storage_class=storage_class;
3575             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3576 
3577             if (q == (Quantum *) NULL)
3578               break;
3579 
3580 
3581             /* Caution: on a Q8 build, this does not distinguish between
3582              * 16-bit colors that differ only in the low byte
3583              */
3584             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3585             {
3586               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3587                   transparent_color.red &&
3588                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3589                   transparent_color.green &&
3590                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3591                   transparent_color.blue)
3592                 {
3593                   SetPixelAlpha(image,TransparentAlpha,q);
3594                 }
3595 
3596 #if 0 /* I have not found a case where this is needed. */
3597               else
3598                 {
3599                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3600                 }
3601 #endif
3602 
3603               q+=GetPixelChannels(image);
3604             }
3605 
3606             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3607                break;
3608           }
3609         }
3610 #endif
3611 
3612       image->storage_class=DirectClass;
3613     }
3614 
3615   for (j = 0; j < 2; j++)
3616   {
3617     if (j == 0)
3618       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3619           MagickTrue : MagickFalse;
3620     else
3621       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3622           MagickTrue : MagickFalse;
3623 
3624     if (status != MagickFalse)
3625       for (i=0; i < (ssize_t) num_text; i++)
3626       {
3627         /* Check for a profile */
3628 
3629         if (logging != MagickFalse)
3630           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3631             "    Reading PNG text chunk");
3632 
3633         if (strlen(text[i].key) > 16 &&
3634             memcmp(text[i].key, "Raw profile type ",17) == 0)
3635           {
3636             const char
3637               *value;
3638 
3639             value=GetImageOption(image_info,"profile:skip");
3640 
3641             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3642             {
3643                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3644                   (int) i,exception);
3645                num_raw_profiles++;
3646                if (logging != MagickFalse)
3647                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3648                    "    Read raw profile %s",text[i].key+17);
3649             }
3650             else
3651             {
3652                if (logging != MagickFalse)
3653                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3654                    "    Skipping raw profile %s",text[i].key+17);
3655             }
3656           }
3657 
3658         else
3659           {
3660             char
3661               *value;
3662 
3663             length=text[i].text_length;
3664             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3665               sizeof(*value));
3666             if (value == (char *) NULL)
3667               {
3668                 png_error(ping,"Memory allocation failed");
3669                 break;
3670               }
3671             *value='\0';
3672             (void) ConcatenateMagickString(value,text[i].text,length+2);
3673 
3674             /* Don't save "density" or "units" property if we have a pHYs
3675              * chunk
3676              */
3677             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3678                 (LocaleCompare(text[i].key,"density") != 0 &&
3679                 LocaleCompare(text[i].key,"units") != 0))
3680                (void) SetImageProperty(image,text[i].key,value,exception);
3681 
3682             if (logging != MagickFalse)
3683             {
3684               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3685                 "      length: %lu\n"
3686                 "      Keyword: %s",
3687                 (unsigned long) length,
3688                 text[i].key);
3689             }
3690 
3691             value=DestroyString(value);
3692           }
3693       }
3694     num_text_total += num_text;
3695   }
3696 
3697 #ifdef MNG_OBJECT_BUFFERS
3698   /*
3699     Store the object if necessary.
3700   */
3701   if (object_id && !mng_info->frozen[object_id])
3702     {
3703       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3704         {
3705           /*
3706             create a new object buffer.
3707           */
3708           mng_info->ob[object_id]=(MngBuffer *)
3709             AcquireMagickMemory(sizeof(MngBuffer));
3710 
3711           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3712             {
3713               mng_info->ob[object_id]->image=(Image *) NULL;
3714               mng_info->ob[object_id]->reference_count=1;
3715             }
3716         }
3717 
3718       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3719           mng_info->ob[object_id]->frozen)
3720         {
3721           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3722              png_error(ping,"Memory allocation failed");
3723 
3724           if (mng_info->ob[object_id]->frozen)
3725             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3726         }
3727 
3728       else
3729         {
3730 
3731           if (mng_info->ob[object_id]->image != (Image *) NULL)
3732             mng_info->ob[object_id]->image=DestroyImage
3733                 (mng_info->ob[object_id]->image);
3734 
3735           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3736             exception);
3737 
3738           if (mng_info->ob[object_id]->image != (Image *) NULL)
3739             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3740 
3741           else
3742             png_error(ping, "Cloning image for object buffer failed");
3743 
3744           if (ping_width > 250000L || ping_height > 250000L)
3745              png_error(ping,"PNG Image dimensions are too large.");
3746 
3747           mng_info->ob[object_id]->width=ping_width;
3748           mng_info->ob[object_id]->height=ping_height;
3749           mng_info->ob[object_id]->color_type=ping_color_type;
3750           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3751           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3752           mng_info->ob[object_id]->compression_method=
3753              ping_compression_method;
3754           mng_info->ob[object_id]->filter_method=ping_filter_method;
3755 
3756           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3757             {
3758               png_colorp
3759                 plte;
3760 
3761               /*
3762                 Copy the PLTE to the object buffer.
3763               */
3764               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3765               mng_info->ob[object_id]->plte_length=number_colors;
3766 
3767               for (i=0; i < number_colors; i++)
3768               {
3769                 mng_info->ob[object_id]->plte[i]=plte[i];
3770               }
3771             }
3772 
3773           else
3774               mng_info->ob[object_id]->plte_length=0;
3775         }
3776     }
3777 #endif
3778 
3779    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3780     * alpha or if a valid tRNS chunk is present, no matter whether there
3781     * is actual transparency present.
3782     */
3783     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3784         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3785         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3786         BlendPixelTrait : UndefinedPixelTrait;
3787 
3788 #if 0  /* I'm not sure what's wrong here but it does not work. */
3789     if (image->alpha_trait != UndefinedPixelTrait)
3790     {
3791       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3792         (void) SetImageType(image,GrayscaleAlphaType,exception);
3793 
3794       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3795         (void) SetImageType(image,PaletteAlphaType,exception);
3796 
3797       else
3798         (void) SetImageType(image,TrueColorAlphaType,exception);
3799     }
3800 
3801     else
3802     {
3803       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3804         (void) SetImageType(image,GrayscaleType,exception);
3805 
3806       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3807         (void) SetImageType(image,PaletteType,exception);
3808 
3809       else
3810         (void) SetImageType(image,TrueColorType,exception);
3811     }
3812 #endif
3813 
3814    /* Set more properties for identify to retrieve */
3815    {
3816      char
3817        msg[MagickPathExtent];
3818 
3819      if (num_text_total != 0)
3820        {
3821          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3822          (void) FormatLocaleString(msg,MagickPathExtent,
3823             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3824          (void) SetImageProperty(image,"png:text",msg,
3825                 exception);
3826        }
3827 
3828      if (num_raw_profiles != 0)
3829        {
3830          (void) FormatLocaleString(msg,MagickPathExtent,
3831             "%d were found", num_raw_profiles);
3832          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3833                 exception);
3834        }
3835 
3836      if (ping_found_cHRM != MagickFalse)
3837        {
3838          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3839             "chunk was found (see Chromaticity, above)");
3840          (void) SetImageProperty(image,"png:cHRM",msg,
3841                 exception);
3842        }
3843 
3844      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3845        {
3846          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3847             "chunk was found (see Background color, above)");
3848          (void) SetImageProperty(image,"png:bKGD",msg,
3849                 exception);
3850        }
3851 
3852      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3853         "chunk was found");
3854 
3855 #if defined(PNG_iCCP_SUPPORTED)
3856      if (ping_found_iCCP != MagickFalse)
3857         (void) SetImageProperty(image,"png:iCCP",msg,
3858                 exception);
3859 #endif
3860 
3861      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3862         (void) SetImageProperty(image,"png:tRNS",msg,
3863                 exception);
3864 
3865 #if defined(PNG_sRGB_SUPPORTED)
3866      if (ping_found_sRGB != MagickFalse)
3867        {
3868          (void) FormatLocaleString(msg,MagickPathExtent,
3869             "intent=%d (%s)",
3870             (int) intent,
3871             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3872          (void) SetImageProperty(image,"png:sRGB",msg,
3873                  exception);
3874        }
3875 #endif
3876 
3877      if (ping_found_gAMA != MagickFalse)
3878        {
3879          (void) FormatLocaleString(msg,MagickPathExtent,
3880             "gamma=%.8g (See Gamma, above)",
3881             file_gamma);
3882          (void) SetImageProperty(image,"png:gAMA",msg,
3883                 exception);
3884        }
3885 
3886 #if defined(PNG_pHYs_SUPPORTED)
3887      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3888        {
3889          (void) FormatLocaleString(msg,MagickPathExtent,
3890             "x_res=%.10g, y_res=%.10g, units=%d",
3891             (double) x_resolution,(double) y_resolution, unit_type);
3892          (void) SetImageProperty(image,"png:pHYs",msg,
3893                 exception);
3894        }
3895 #endif
3896 
3897 #if defined(PNG_oFFs_SUPPORTED)
3898      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3899        {
3900          (void) FormatLocaleString(msg,MagickPathExtent,"x_off=%.20g, y_off=%.20g",
3901             (double) image->page.x,(double) image->page.y);
3902          (void) SetImageProperty(image,"png:oFFs",msg,
3903                 exception);
3904        }
3905 #endif
3906 
3907 #if defined(PNG_tIME_SUPPORTED)
3908      read_tIME_chunk(image,ping,end_info,exception);
3909 #endif
3910 
3911      if ((image->page.width != 0 && image->page.width != image->columns) ||
3912          (image->page.height != 0 && image->page.height != image->rows))
3913        {
3914          (void) FormatLocaleString(msg,MagickPathExtent,
3915             "width=%.20g, height=%.20g",
3916             (double) image->page.width,(double) image->page.height);
3917          (void) SetImageProperty(image,"png:vpAg",msg,
3918                 exception);
3919        }
3920    }
3921 
3922   /*
3923     Relinquish resources.
3924   */
3925   png_destroy_read_struct(&ping,&ping_info,&end_info);
3926 
3927   pixel_info=RelinquishVirtualMemory(pixel_info);
3928 
3929   if (logging != MagickFalse)
3930     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3931       "  exit ReadOnePNGImage()");
3932 
3933 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3934   UnlockSemaphoreInfo(ping_semaphore);
3935 #endif
3936 
3937   /* }  for navigation to beginning of SETJMP-protected block, revert to
3938    *    Throwing an Exception when an error occurs.
3939    */
3940 
3941   return(image);
3942 
3943 /* end of reading one PNG image */
3944 }
3945 
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)3946 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3947 {
3948   Image
3949     *image;
3950 
3951   MagickBooleanType
3952     have_mng_structure,
3953     logging,
3954     status;
3955 
3956   MngInfo
3957     *mng_info;
3958 
3959   char
3960     magic_number[MagickPathExtent];
3961 
3962   ssize_t
3963     count;
3964 
3965   /*
3966     Open image file.
3967   */
3968   assert(image_info != (const ImageInfo *) NULL);
3969   assert(image_info->signature == MagickCoreSignature);
3970 
3971   if (image_info->debug != MagickFalse)
3972     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3973       image_info->filename);
3974 
3975   assert(exception != (ExceptionInfo *) NULL);
3976   assert(exception->signature == MagickCoreSignature);
3977   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3978   image=AcquireImage(image_info,exception);
3979   mng_info=(MngInfo *) NULL;
3980   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3981 
3982   if (status == MagickFalse)
3983     ThrowReaderException(FileOpenError,"UnableToOpenFile");
3984 
3985   /*
3986     Verify PNG signature.
3987   */
3988   count=ReadBlob(image,8,(unsigned char *) magic_number);
3989 
3990   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3991     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3992 
3993   /*
3994     Allocate a MngInfo structure.
3995   */
3996   have_mng_structure=MagickFalse;
3997   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3998 
3999   if (mng_info == (MngInfo *) NULL)
4000     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4001 
4002   /*
4003     Initialize members of the MngInfo structure.
4004   */
4005   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4006   mng_info->image=image;
4007   have_mng_structure=MagickTrue;
4008 
4009   image=ReadOnePNGImage(mng_info,image_info,exception);
4010   MngInfoFreeStruct(mng_info,&have_mng_structure);
4011 
4012   if (image == (Image *) NULL)
4013     {
4014       if (logging != MagickFalse)
4015         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4016           "exit ReadPNGImage() with error");
4017 
4018       return((Image *) NULL);
4019     }
4020 
4021   (void) CloseBlob(image);
4022 
4023   if ((image->columns == 0) || (image->rows == 0))
4024     {
4025       if (logging != MagickFalse)
4026         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4027           "exit ReadPNGImage() with error.");
4028 
4029       ThrowReaderException(CorruptImageError,"CorruptImage");
4030     }
4031 
4032   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4033       ((image->gamma < .45) || (image->gamma > .46)) &&
4034            !(image->chromaticity.red_primary.x>0.6399f &&
4035            image->chromaticity.red_primary.x<0.6401f &&
4036            image->chromaticity.red_primary.y>0.3299f &&
4037            image->chromaticity.red_primary.y<0.3301f &&
4038            image->chromaticity.green_primary.x>0.2999f &&
4039            image->chromaticity.green_primary.x<0.3001f &&
4040            image->chromaticity.green_primary.y>0.5999f &&
4041            image->chromaticity.green_primary.y<0.6001f &&
4042            image->chromaticity.blue_primary.x>0.1499f &&
4043            image->chromaticity.blue_primary.x<0.1501f &&
4044            image->chromaticity.blue_primary.y>0.0599f &&
4045            image->chromaticity.blue_primary.y<0.0601f &&
4046            image->chromaticity.white_point.x>0.3126f &&
4047            image->chromaticity.white_point.x<0.3128f &&
4048            image->chromaticity.white_point.y>0.3289f &&
4049            image->chromaticity.white_point.y<0.3291f))
4050     {
4051        SetImageColorspace(image,RGBColorspace,exception);
4052     }
4053 
4054   if (logging != MagickFalse)
4055     {
4056        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4057            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4058                (double) image->page.width,(double) image->page.height,
4059                (double) image->page.x,(double) image->page.y);
4060        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4061            "  image->colorspace: %d", (int) image->colorspace);
4062     }
4063 
4064   if (logging != MagickFalse)
4065     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4066 
4067   return(image);
4068 }
4069 
4070 
4071 
4072 #if defined(JNG_SUPPORTED)
4073 /*
4074 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4075 %                                                                             %
4076 %                                                                             %
4077 %                                                                             %
4078 %   R e a d O n e J N G I m a g e                                             %
4079 %                                                                             %
4080 %                                                                             %
4081 %                                                                             %
4082 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4083 %
4084 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4085 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4086 %  necessary for the new Image structure and returns a pointer to the new
4087 %  image.
4088 %
4089 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4090 %
4091 %  The format of the ReadOneJNGImage method is:
4092 %
4093 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4094 %         ExceptionInfo *exception)
4095 %
4096 %  A description of each parameter follows:
4097 %
4098 %    o mng_info: Specifies a pointer to a MngInfo structure.
4099 %
4100 %    o image_info: the image info.
4101 %
4102 %    o exception: return any errors or warnings in this structure.
4103 %
4104 */
ReadOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)4105 static Image *ReadOneJNGImage(MngInfo *mng_info,
4106     const ImageInfo *image_info, ExceptionInfo *exception)
4107 {
4108   Image
4109     *alpha_image,
4110     *color_image,
4111     *image,
4112     *jng_image;
4113 
4114   ImageInfo
4115     *alpha_image_info,
4116     *color_image_info;
4117 
4118   MagickBooleanType
4119     logging;
4120 
4121   ssize_t
4122     y;
4123 
4124   MagickBooleanType
4125     status;
4126 
4127   png_uint_32
4128     jng_height,
4129     jng_width;
4130 
4131   png_byte
4132     jng_color_type,
4133     jng_image_sample_depth,
4134     jng_image_compression_method,
4135     jng_image_interlace_method,
4136     jng_alpha_sample_depth,
4137     jng_alpha_compression_method,
4138     jng_alpha_filter_method,
4139     jng_alpha_interlace_method;
4140 
4141   register const Quantum
4142     *s;
4143 
4144   register ssize_t
4145     i,
4146     x;
4147 
4148   register Quantum
4149     *q;
4150 
4151   register unsigned char
4152     *p;
4153 
4154   unsigned int
4155     read_JSEP,
4156     reading_idat;
4157 
4158   size_t
4159     length;
4160 
4161   jng_alpha_compression_method=0;
4162   jng_alpha_sample_depth=8;
4163   jng_color_type=0;
4164   jng_height=0;
4165   jng_width=0;
4166   alpha_image=(Image *) NULL;
4167   color_image=(Image *) NULL;
4168   alpha_image_info=(ImageInfo *) NULL;
4169   color_image_info=(ImageInfo *) NULL;
4170 
4171   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4172     "  Enter ReadOneJNGImage()");
4173 
4174   image=mng_info->image;
4175 
4176   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4177     {
4178       /*
4179         Allocate next image structure.
4180       */
4181       if (logging != MagickFalse)
4182         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4183            "  AcquireNextImage()");
4184 
4185       AcquireNextImage(image_info,image,exception);
4186 
4187       if (GetNextImageInList(image) == (Image *) NULL)
4188         return((Image *) NULL);
4189 
4190       image=SyncNextImageInList(image);
4191     }
4192   mng_info->image=image;
4193 
4194   /*
4195     Signature bytes have already been read.
4196   */
4197 
4198   read_JSEP=MagickFalse;
4199   reading_idat=MagickFalse;
4200   for (;;)
4201   {
4202     char
4203       type[MagickPathExtent];
4204 
4205     unsigned char
4206       *chunk;
4207 
4208     unsigned int
4209       count;
4210 
4211     /*
4212       Read a new JNG chunk.
4213     */
4214     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4215       2*GetBlobSize(image));
4216 
4217     if (status == MagickFalse)
4218       break;
4219 
4220     type[0]='\0';
4221     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4222     length=ReadBlobMSBLong(image);
4223     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4224 
4225     if (logging != MagickFalse)
4226       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4227         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4228         type[0],type[1],type[2],type[3],(double) length);
4229 
4230     if (length > PNG_UINT_31_MAX || count == 0)
4231       ThrowReaderException(CorruptImageError,"CorruptImage");
4232 
4233     p=NULL;
4234     chunk=(unsigned char *) NULL;
4235 
4236     if (length != 0)
4237       {
4238         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4239 
4240         if (chunk == (unsigned char *) NULL)
4241           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4242 
4243         for (i=0; i < (ssize_t) length; i++)
4244           chunk[i]=(unsigned char) ReadBlobByte(image);
4245 
4246         p=chunk;
4247       }
4248 
4249     (void) ReadBlobMSBLong(image);  /* read crc word */
4250 
4251     if (memcmp(type,mng_JHDR,4) == 0)
4252       {
4253         if (length == 16)
4254           {
4255             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4256               (p[2] << 8) | p[3]);
4257             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4258               (p[6] << 8) | p[7]);
4259             if ((jng_width == 0) || (jng_height == 0))
4260               ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4261             jng_color_type=p[8];
4262             jng_image_sample_depth=p[9];
4263             jng_image_compression_method=p[10];
4264             jng_image_interlace_method=p[11];
4265 
4266             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4267               NoInterlace;
4268 
4269             jng_alpha_sample_depth=p[12];
4270             jng_alpha_compression_method=p[13];
4271             jng_alpha_filter_method=p[14];
4272             jng_alpha_interlace_method=p[15];
4273 
4274             if (logging != MagickFalse)
4275               {
4276                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4277                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4278                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4279                   "    jng_image_compression_method:%3d",
4280                   (unsigned long) jng_width, (unsigned long) jng_height,
4281                   jng_color_type, jng_image_sample_depth,
4282                   jng_image_compression_method);
4283 
4284                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4285                   "    jng_image_interlace_method:  %3d"
4286                   "    jng_alpha_sample_depth:      %3d",
4287                   jng_image_interlace_method,
4288                   jng_alpha_sample_depth);
4289 
4290                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4291                   "    jng_alpha_compression_method:%3d\n"
4292                   "    jng_alpha_filter_method:     %3d\n"
4293                   "    jng_alpha_interlace_method:  %3d",
4294                   jng_alpha_compression_method,
4295                   jng_alpha_filter_method,
4296                   jng_alpha_interlace_method);
4297               }
4298           }
4299 
4300         if (length != 0)
4301           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4302 
4303         continue;
4304       }
4305 
4306 
4307     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4308         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4309          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4310       {
4311         /*
4312            o create color_image
4313            o open color_blob, attached to color_image
4314            o if (color type has alpha)
4315                open alpha_blob, attached to alpha_image
4316         */
4317 
4318         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4319 
4320         if (color_image_info == (ImageInfo *) NULL)
4321           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4322 
4323         GetImageInfo(color_image_info);
4324         color_image=AcquireImage(color_image_info,exception);
4325 
4326         if (color_image == (Image *) NULL)
4327           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4328 
4329         if (logging != MagickFalse)
4330           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4331             "    Creating color_blob.");
4332 
4333         (void) AcquireUniqueFilename(color_image->filename);
4334         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4335           exception);
4336 
4337         if (status == MagickFalse)
4338           return((Image *) NULL);
4339 
4340         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4341           {
4342             alpha_image_info=(ImageInfo *)
4343               AcquireMagickMemory(sizeof(ImageInfo));
4344 
4345             if (alpha_image_info == (ImageInfo *) NULL)
4346               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4347 
4348             GetImageInfo(alpha_image_info);
4349             alpha_image=AcquireImage(alpha_image_info,exception);
4350 
4351             if (alpha_image == (Image *) NULL)
4352               {
4353                 alpha_image=DestroyImage(alpha_image);
4354                 ThrowReaderException(ResourceLimitError,
4355                   "MemoryAllocationFailed");
4356               }
4357 
4358             if (logging != MagickFalse)
4359               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4360                 "    Creating alpha_blob.");
4361 
4362             (void) AcquireUniqueFilename(alpha_image->filename);
4363             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4364               exception);
4365 
4366             if (status == MagickFalse)
4367               return((Image *) NULL);
4368 
4369             if (jng_alpha_compression_method == 0)
4370               {
4371                 unsigned char
4372                   data[18];
4373 
4374                 if (logging != MagickFalse)
4375                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4376                     "    Writing IHDR chunk to alpha_blob.");
4377 
4378                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4379                   "\211PNG\r\n\032\n");
4380 
4381                 (void) WriteBlobMSBULong(alpha_image,13L);
4382                 PNGType(data,mng_IHDR);
4383                 LogPNGChunk(logging,mng_IHDR,13L);
4384                 PNGLong(data+4,jng_width);
4385                 PNGLong(data+8,jng_height);
4386                 data[12]=jng_alpha_sample_depth;
4387                 data[13]=0; /* color_type gray */
4388                 data[14]=0; /* compression method 0 */
4389                 data[15]=0; /* filter_method 0 */
4390                 data[16]=0; /* interlace_method 0 */
4391                 (void) WriteBlob(alpha_image,17,data);
4392                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4393               }
4394           }
4395         reading_idat=MagickTrue;
4396       }
4397 
4398     if (memcmp(type,mng_JDAT,4) == 0)
4399       {
4400         /* Copy chunk to color_image->blob */
4401 
4402         if (logging != MagickFalse)
4403           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4404             "    Copying JDAT chunk data to color_blob.");
4405 
4406         (void) WriteBlob(color_image,length,chunk);
4407 
4408         if (length != 0)
4409           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4410 
4411         continue;
4412       }
4413 
4414     if (memcmp(type,mng_IDAT,4) == 0)
4415       {
4416         png_byte
4417            data[5];
4418 
4419         /* Copy IDAT header and chunk data to alpha_image->blob */
4420 
4421         if (alpha_image != NULL && image_info->ping == MagickFalse)
4422           {
4423             if (logging != MagickFalse)
4424               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4425                 "    Copying IDAT chunk data to alpha_blob.");
4426 
4427             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4428             PNGType(data,mng_IDAT);
4429             LogPNGChunk(logging,mng_IDAT,length);
4430             (void) WriteBlob(alpha_image,4,data);
4431             (void) WriteBlob(alpha_image,length,chunk);
4432             (void) WriteBlobMSBULong(alpha_image,
4433               crc32(crc32(0,data,4),chunk,(uInt) length));
4434           }
4435 
4436         if (length != 0)
4437           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4438 
4439         continue;
4440       }
4441 
4442     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4443       {
4444         /* Copy chunk data to alpha_image->blob */
4445 
4446         if (alpha_image != NULL && image_info->ping == MagickFalse)
4447           {
4448             if (logging != MagickFalse)
4449               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4450                 "    Copying JDAA chunk data to alpha_blob.");
4451 
4452             (void) WriteBlob(alpha_image,length,chunk);
4453           }
4454 
4455         if (length != 0)
4456           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4457 
4458         continue;
4459       }
4460 
4461     if (memcmp(type,mng_JSEP,4) == 0)
4462       {
4463         read_JSEP=MagickTrue;
4464 
4465         if (length != 0)
4466           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4467 
4468         continue;
4469       }
4470 
4471     if (memcmp(type,mng_bKGD,4) == 0)
4472       {
4473         if (length == 2)
4474           {
4475             image->background_color.red=ScaleCharToQuantum(p[1]);
4476             image->background_color.green=image->background_color.red;
4477             image->background_color.blue=image->background_color.red;
4478           }
4479 
4480         if (length == 6)
4481           {
4482             image->background_color.red=ScaleCharToQuantum(p[1]);
4483             image->background_color.green=ScaleCharToQuantum(p[3]);
4484             image->background_color.blue=ScaleCharToQuantum(p[5]);
4485           }
4486 
4487         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4488         continue;
4489       }
4490 
4491     if (memcmp(type,mng_gAMA,4) == 0)
4492       {
4493         if (length == 4)
4494           image->gamma=((float) mng_get_long(p))*0.00001;
4495 
4496         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4497         continue;
4498       }
4499 
4500     if (memcmp(type,mng_cHRM,4) == 0)
4501       {
4502         if (length == 32)
4503           {
4504             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4505             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4506             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4507             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4508             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4509             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4510             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4511             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4512           }
4513 
4514         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4515         continue;
4516       }
4517 
4518     if (memcmp(type,mng_sRGB,4) == 0)
4519       {
4520         if (length == 1)
4521           {
4522             image->rendering_intent=
4523               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4524             image->gamma=1.000f/2.200f;
4525             image->chromaticity.red_primary.x=0.6400f;
4526             image->chromaticity.red_primary.y=0.3300f;
4527             image->chromaticity.green_primary.x=0.3000f;
4528             image->chromaticity.green_primary.y=0.6000f;
4529             image->chromaticity.blue_primary.x=0.1500f;
4530             image->chromaticity.blue_primary.y=0.0600f;
4531             image->chromaticity.white_point.x=0.3127f;
4532             image->chromaticity.white_point.y=0.3290f;
4533           }
4534 
4535         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4536         continue;
4537       }
4538 
4539     if (memcmp(type,mng_oFFs,4) == 0)
4540       {
4541         if (length > 8)
4542           {
4543             image->page.x=(ssize_t) mng_get_long(p);
4544             image->page.y=(ssize_t) mng_get_long(&p[4]);
4545 
4546             if ((int) p[8] != 0)
4547               {
4548                 image->page.x/=10000;
4549                 image->page.y/=10000;
4550               }
4551           }
4552 
4553         if (length != 0)
4554           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4555 
4556         continue;
4557       }
4558 
4559     if (memcmp(type,mng_pHYs,4) == 0)
4560       {
4561         if (length > 8)
4562           {
4563             image->resolution.x=(double) mng_get_long(p);
4564             image->resolution.y=(double) mng_get_long(&p[4]);
4565             if ((int) p[8] == PNG_RESOLUTION_METER)
4566               {
4567                 image->units=PixelsPerCentimeterResolution;
4568                 image->resolution.x=image->resolution.x/100.0f;
4569                 image->resolution.y=image->resolution.y/100.0f;
4570               }
4571           }
4572 
4573         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4574         continue;
4575       }
4576 
4577 #if 0
4578     if (memcmp(type,mng_iCCP,4) == 0)
4579       {
4580         /* To do: */
4581         if (length != 0)
4582           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4583 
4584         continue;
4585       }
4586 #endif
4587 
4588     if (length != 0)
4589       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4590 
4591     if (memcmp(type,mng_IEND,4))
4592       continue;
4593 
4594     break;
4595   }
4596 
4597 
4598   /* IEND found */
4599 
4600   /*
4601     Finish up reading image data:
4602 
4603        o read main image from color_blob.
4604 
4605        o close color_blob.
4606 
4607        o if (color_type has alpha)
4608             if alpha_encoding is PNG
4609                read secondary image from alpha_blob via ReadPNG
4610             if alpha_encoding is JPEG
4611                read secondary image from alpha_blob via ReadJPEG
4612 
4613        o close alpha_blob.
4614 
4615        o copy intensity of secondary image into
4616          alpha samples of main image.
4617 
4618        o destroy the secondary image.
4619   */
4620 
4621   if (color_image_info == (ImageInfo *) NULL)
4622     {
4623       assert(color_image == (Image *) NULL);
4624       assert(alpha_image == (Image *) NULL);
4625       return((Image *) NULL);
4626     }
4627 
4628   if (color_image == (Image *) NULL)
4629     {
4630       assert(alpha_image == (Image *) NULL);
4631       return((Image *) NULL);
4632     }
4633 
4634   (void) SeekBlob(color_image,0,SEEK_SET);
4635 
4636   if (logging != MagickFalse)
4637     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4638       "    Reading jng_image from color_blob.");
4639 
4640   assert(color_image_info != (ImageInfo *) NULL);
4641   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4642     color_image->filename);
4643 
4644   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4645   jng_image=ReadImage(color_image_info,exception);
4646 
4647   (void) RelinquishUniqueFileResource(color_image->filename);
4648   color_image=DestroyImage(color_image);
4649   color_image_info=DestroyImageInfo(color_image_info);
4650 
4651   if (jng_image == (Image *) NULL)
4652     return((Image *) NULL);
4653 
4654   if (logging != MagickFalse)
4655     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4656       "    Copying jng_image pixels to main image.");
4657 
4658   image->rows=jng_height;
4659   image->columns=jng_width;
4660 
4661   status=SetImageExtent(image,image->columns,image->rows,exception);
4662   if (status == MagickFalse)
4663     return(DestroyImageList(image));
4664 
4665   for (y=0; y < (ssize_t) image->rows; y++)
4666   {
4667     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4668     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4669     for (x=(ssize_t) image->columns; x != 0; x--)
4670     {
4671       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4672       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4673       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4674       q+=GetPixelChannels(image);
4675       s+=GetPixelChannels(jng_image);
4676     }
4677 
4678     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4679       break;
4680   }
4681 
4682   jng_image=DestroyImage(jng_image);
4683 
4684   if (image_info->ping == MagickFalse)
4685     {
4686      if (jng_color_type >= 12)
4687        {
4688          if (jng_alpha_compression_method == 0)
4689            {
4690              png_byte
4691                data[5];
4692              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4693              PNGType(data,mng_IEND);
4694              LogPNGChunk(logging,mng_IEND,0L);
4695              (void) WriteBlob(alpha_image,4,data);
4696              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4697            }
4698 
4699          (void) CloseBlob(alpha_image);
4700 
4701          if (logging != MagickFalse)
4702            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4703              "    Reading alpha from alpha_blob.");
4704 
4705          (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
4706            "%s",alpha_image->filename);
4707 
4708          jng_image=ReadImage(alpha_image_info,exception);
4709 
4710          if (jng_image != (Image *) NULL)
4711            for (y=0; y < (ssize_t) image->rows; y++)
4712            {
4713              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4714                exception);
4715              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4716 
4717              if (image->alpha_trait != UndefinedPixelTrait)
4718                for (x=(ssize_t) image->columns; x != 0; x--)
4719                {
4720                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4721                   q+=GetPixelChannels(image);
4722                   s+=GetPixelChannels(jng_image);
4723                }
4724 
4725              else
4726                for (x=(ssize_t) image->columns; x != 0; x--)
4727                {
4728                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4729                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4730                     image->alpha_trait=BlendPixelTrait;
4731                   q+=GetPixelChannels(image);
4732                   s+=GetPixelChannels(jng_image);
4733                }
4734 
4735              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4736                break;
4737            }
4738          (void) RelinquishUniqueFileResource(alpha_image->filename);
4739          alpha_image=DestroyImage(alpha_image);
4740          alpha_image_info=DestroyImageInfo(alpha_image_info);
4741          if (jng_image != (Image *) NULL)
4742            jng_image=DestroyImage(jng_image);
4743        }
4744     }
4745 
4746   /* Read the JNG image.  */
4747 
4748   if (mng_info->mng_type == 0)
4749     {
4750       mng_info->mng_width=jng_width;
4751       mng_info->mng_height=jng_height;
4752     }
4753 
4754   if (image->page.width == 0 && image->page.height == 0)
4755     {
4756       image->page.width=jng_width;
4757       image->page.height=jng_height;
4758     }
4759 
4760   if (image->page.x == 0 && image->page.y == 0)
4761     {
4762       image->page.x=mng_info->x_off[mng_info->object_id];
4763       image->page.y=mng_info->y_off[mng_info->object_id];
4764     }
4765 
4766   else
4767     {
4768       image->page.y=mng_info->y_off[mng_info->object_id];
4769     }
4770 
4771   mng_info->image_found++;
4772   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4773     2*GetBlobSize(image));
4774 
4775   if (status == MagickFalse)
4776     return((Image *) NULL);
4777 
4778   if (logging != MagickFalse)
4779     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4780       "  exit ReadOneJNGImage()");
4781 
4782   return(image);
4783 }
4784 
4785 /*
4786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4787 %                                                                             %
4788 %                                                                             %
4789 %                                                                             %
4790 %   R e a d J N G I m a g e                                                   %
4791 %                                                                             %
4792 %                                                                             %
4793 %                                                                             %
4794 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4795 %
4796 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4797 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4798 %  necessary for the new Image structure and returns a pointer to the new
4799 %  image.
4800 %
4801 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4802 %
4803 %  The format of the ReadJNGImage method is:
4804 %
4805 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4806 %         *exception)
4807 %
4808 %  A description of each parameter follows:
4809 %
4810 %    o image_info: the image info.
4811 %
4812 %    o exception: return any errors or warnings in this structure.
4813 %
4814 */
4815 
ReadJNGImage(const ImageInfo * image_info,ExceptionInfo * exception)4816 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4817 {
4818   Image
4819     *image;
4820 
4821   MagickBooleanType
4822     have_mng_structure,
4823     logging,
4824     status;
4825 
4826   MngInfo
4827     *mng_info;
4828 
4829   char
4830     magic_number[MagickPathExtent];
4831 
4832   size_t
4833     count;
4834 
4835   /*
4836     Open image file.
4837   */
4838   assert(image_info != (const ImageInfo *) NULL);
4839   assert(image_info->signature == MagickCoreSignature);
4840   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4841   assert(exception != (ExceptionInfo *) NULL);
4842   assert(exception->signature == MagickCoreSignature);
4843   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4844   image=AcquireImage(image_info,exception);
4845   mng_info=(MngInfo *) NULL;
4846   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4847 
4848   if (status == MagickFalse)
4849     return((Image *) NULL);
4850 
4851   if (LocaleCompare(image_info->magick,"JNG") != 0)
4852     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4853 
4854   /* Verify JNG signature.  */
4855 
4856   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4857 
4858   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4859     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4860 
4861   /* Allocate a MngInfo structure.  */
4862 
4863   have_mng_structure=MagickFalse;
4864   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4865 
4866   if (mng_info == (MngInfo *) NULL)
4867     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4868 
4869   /* Initialize members of the MngInfo structure.  */
4870 
4871   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4872   have_mng_structure=MagickTrue;
4873 
4874   mng_info->image=image;
4875   image=ReadOneJNGImage(mng_info,image_info,exception);
4876   MngInfoFreeStruct(mng_info,&have_mng_structure);
4877 
4878   if (image == (Image *) NULL)
4879     {
4880       if (logging != MagickFalse)
4881         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4882           "exit ReadJNGImage() with error");
4883 
4884       return((Image *) NULL);
4885     }
4886   (void) CloseBlob(image);
4887 
4888   if (image->columns == 0 || image->rows == 0)
4889     {
4890       if (logging != MagickFalse)
4891         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4892           "exit ReadJNGImage() with error");
4893 
4894       ThrowReaderException(CorruptImageError,"CorruptImage");
4895     }
4896 
4897   if (logging != MagickFalse)
4898     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4899 
4900   return(image);
4901 }
4902 #endif
4903 
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)4904 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4905 {
4906   char
4907     page_geometry[MagickPathExtent];
4908 
4909   Image
4910     *image;
4911 
4912   MagickBooleanType
4913     logging,
4914     have_mng_structure;
4915 
4916   volatile int
4917     first_mng_object,
4918     object_id,
4919     term_chunk_found,
4920     skip_to_iend;
4921 
4922   volatile ssize_t
4923     image_count=0;
4924 
4925   MagickBooleanType
4926     status;
4927 
4928   MagickOffsetType
4929     offset;
4930 
4931   MngInfo
4932     *mng_info;
4933 
4934   MngBox
4935     default_fb,
4936     fb,
4937     previous_fb;
4938 
4939 #if defined(MNG_INSERT_LAYERS)
4940   PixelInfo
4941     mng_background_color;
4942 #endif
4943 
4944   register unsigned char
4945     *p;
4946 
4947   register ssize_t
4948     i;
4949 
4950   size_t
4951     count;
4952 
4953   ssize_t
4954     loop_level;
4955 
4956   volatile short
4957     skipping_loop;
4958 
4959 #if defined(MNG_INSERT_LAYERS)
4960   unsigned int
4961     mandatory_back=0;
4962 #endif
4963 
4964   volatile unsigned int
4965 #ifdef MNG_OBJECT_BUFFERS
4966     mng_background_object=0,
4967 #endif
4968     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4969 
4970   size_t
4971     default_frame_timeout,
4972     frame_timeout,
4973 #if defined(MNG_INSERT_LAYERS)
4974     image_height,
4975     image_width,
4976 #endif
4977     length;
4978 
4979   /* These delays are all measured in image ticks_per_second,
4980    * not in MNG ticks_per_second
4981    */
4982   volatile size_t
4983     default_frame_delay,
4984     final_delay,
4985     final_image_delay,
4986     frame_delay,
4987 #if defined(MNG_INSERT_LAYERS)
4988     insert_layers,
4989 #endif
4990     mng_iterations=1,
4991     simplicity=0,
4992     subframe_height=0,
4993     subframe_width=0;
4994 
4995   previous_fb.top=0;
4996   previous_fb.bottom=0;
4997   previous_fb.left=0;
4998   previous_fb.right=0;
4999   default_fb.top=0;
5000   default_fb.bottom=0;
5001   default_fb.left=0;
5002   default_fb.right=0;
5003 
5004   /* Open image file.  */
5005 
5006   assert(image_info != (const ImageInfo *) NULL);
5007   assert(image_info->signature == MagickCoreSignature);
5008   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
5009   assert(exception != (ExceptionInfo *) NULL);
5010   assert(exception->signature == MagickCoreSignature);
5011   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5012   image=AcquireImage(image_info,exception);
5013   mng_info=(MngInfo *) NULL;
5014   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5015 
5016   if (status == MagickFalse)
5017     return((Image *) NULL);
5018 
5019   first_mng_object=MagickFalse;
5020   skipping_loop=(-1);
5021   have_mng_structure=MagickFalse;
5022 
5023   /* Allocate a MngInfo structure.  */
5024 
5025   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5026 
5027   if (mng_info == (MngInfo *) NULL)
5028     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5029 
5030   /* Initialize members of the MngInfo structure.  */
5031 
5032   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5033   mng_info->image=image;
5034   have_mng_structure=MagickTrue;
5035 
5036   if (LocaleCompare(image_info->magick,"MNG") == 0)
5037     {
5038       char
5039         magic_number[MagickPathExtent];
5040 
5041       /* Verify MNG signature.  */
5042       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5043       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5044         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5045 
5046       /* Initialize some nonzero members of the MngInfo structure.  */
5047       for (i=0; i < MNG_MAX_OBJECTS; i++)
5048       {
5049         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5050         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5051       }
5052       mng_info->exists[0]=MagickTrue;
5053     }
5054 
5055   first_mng_object=MagickTrue;
5056   mng_type=0;
5057 #if defined(MNG_INSERT_LAYERS)
5058   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5059 #endif
5060   default_frame_delay=0;
5061   default_frame_timeout=0;
5062   frame_delay=0;
5063   final_delay=1;
5064   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5065   object_id=0;
5066   skip_to_iend=MagickFalse;
5067   term_chunk_found=MagickFalse;
5068   mng_info->framing_mode=1;
5069 #if defined(MNG_INSERT_LAYERS)
5070   mandatory_back=MagickFalse;
5071 #endif
5072 #if defined(MNG_INSERT_LAYERS)
5073   mng_background_color=image->background_color;
5074 #endif
5075   default_fb=mng_info->frame;
5076   previous_fb=mng_info->frame;
5077   do
5078   {
5079     char
5080       type[MagickPathExtent];
5081 
5082     if (LocaleCompare(image_info->magick,"MNG") == 0)
5083       {
5084         unsigned char
5085           *chunk;
5086 
5087         /*
5088           Read a new chunk.
5089         */
5090         type[0]='\0';
5091         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5092         length=ReadBlobMSBLong(image);
5093         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5094 
5095         if (logging != MagickFalse)
5096           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5097            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5098            type[0],type[1],type[2],type[3],(double) length);
5099 
5100         if (length > PNG_UINT_31_MAX)
5101           {
5102             status=MagickFalse;
5103             break;
5104           }
5105 
5106         if (count == 0)
5107           ThrowReaderException(CorruptImageError,"CorruptImage");
5108 
5109         p=NULL;
5110         chunk=(unsigned char *) NULL;
5111 
5112         if (length != 0)
5113           {
5114             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5115 
5116             if (chunk == (unsigned char *) NULL)
5117               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5118 
5119             for (i=0; i < (ssize_t) length; i++)
5120               chunk[i]=(unsigned char) ReadBlobByte(image);
5121 
5122             p=chunk;
5123           }
5124 
5125         (void) ReadBlobMSBLong(image);  /* read crc word */
5126 
5127 #if !defined(JNG_SUPPORTED)
5128         if (memcmp(type,mng_JHDR,4) == 0)
5129           {
5130             skip_to_iend=MagickTrue;
5131 
5132             if (mng_info->jhdr_warning == 0)
5133               (void) ThrowMagickException(exception,GetMagickModule(),
5134                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5135 
5136             mng_info->jhdr_warning++;
5137           }
5138 #endif
5139         if (memcmp(type,mng_DHDR,4) == 0)
5140           {
5141             skip_to_iend=MagickTrue;
5142 
5143             if (mng_info->dhdr_warning == 0)
5144               (void) ThrowMagickException(exception,GetMagickModule(),
5145                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5146 
5147             mng_info->dhdr_warning++;
5148           }
5149         if (memcmp(type,mng_MEND,4) == 0)
5150           break;
5151 
5152         if (skip_to_iend)
5153           {
5154             if (memcmp(type,mng_IEND,4) == 0)
5155               skip_to_iend=MagickFalse;
5156 
5157             if (length != 0)
5158               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5159 
5160             if (logging != MagickFalse)
5161               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5162                 "  Skip to IEND.");
5163 
5164             continue;
5165           }
5166 
5167         if (memcmp(type,mng_MHDR,4) == 0)
5168           {
5169             if (length != 28)
5170               {
5171                 if (chunk)
5172                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5173                 ThrowReaderException(CorruptImageError,"CorruptImage");
5174               }
5175 
5176             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5177                 (p[2] << 8) | p[3]);
5178 
5179             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5180                 (p[6] << 8) | p[7]);
5181 
5182             if (logging != MagickFalse)
5183               {
5184                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5185                   "  MNG width: %.20g",(double) mng_info->mng_width);
5186                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5187                   "  MNG height: %.20g",(double) mng_info->mng_height);
5188               }
5189 
5190             p+=8;
5191             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5192 
5193             if (mng_info->ticks_per_second == 0)
5194               default_frame_delay=0;
5195 
5196             else
5197               default_frame_delay=1UL*image->ticks_per_second/
5198                 mng_info->ticks_per_second;
5199 
5200             frame_delay=default_frame_delay;
5201             simplicity=0;
5202 
5203             p+=16;
5204             simplicity=(size_t) mng_get_long(p);
5205 
5206             mng_type=1;    /* Full MNG */
5207 
5208             if ((simplicity != 0) && ((simplicity | 11) == 11))
5209               mng_type=2; /* LC */
5210 
5211             if ((simplicity != 0) && ((simplicity | 9) == 9))
5212               mng_type=3; /* VLC */
5213 
5214 #if defined(MNG_INSERT_LAYERS)
5215             if (mng_type != 3)
5216               insert_layers=MagickTrue;
5217 #endif
5218             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5219               {
5220                 /* Allocate next image structure.  */
5221                 AcquireNextImage(image_info,image,exception);
5222 
5223                 if (GetNextImageInList(image) == (Image *) NULL)
5224                   return((Image *) NULL);
5225 
5226                 image=SyncNextImageInList(image);
5227                 mng_info->image=image;
5228               }
5229 
5230             if ((mng_info->mng_width > 65535L) ||
5231                 (mng_info->mng_height > 65535L))
5232               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5233 
5234             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5235               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5236               mng_info->mng_height);
5237 
5238             mng_info->frame.left=0;
5239             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5240             mng_info->frame.top=0;
5241             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5242             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5243 
5244             for (i=0; i < MNG_MAX_OBJECTS; i++)
5245               mng_info->object_clip[i]=mng_info->frame;
5246 
5247             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5248             continue;
5249           }
5250 
5251         if (memcmp(type,mng_TERM,4) == 0)
5252           {
5253             int
5254               repeat=0;
5255 
5256             if (length != 0)
5257               repeat=p[0];
5258 
5259             if (repeat == 3)
5260               {
5261                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5262                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5263 
5264                 if (mng_iterations == PNG_UINT_31_MAX)
5265                   mng_iterations=0;
5266 
5267                 image->iterations=mng_iterations;
5268                 term_chunk_found=MagickTrue;
5269               }
5270 
5271             if (logging != MagickFalse)
5272               {
5273                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5274                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5275                   repeat,(double) final_delay, (double) image->iterations);
5276               }
5277 
5278             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5279             continue;
5280           }
5281         if (memcmp(type,mng_DEFI,4) == 0)
5282           {
5283             if (mng_type == 3)
5284               (void) ThrowMagickException(exception,GetMagickModule(),
5285                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5286                 image->filename);
5287 
5288             if (length < 2)
5289               {
5290                 if (chunk)
5291                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5292                 ThrowReaderException(CorruptImageError,"CorruptImage");
5293               }
5294 
5295             object_id=(p[0] << 8) | p[1];
5296 
5297             if (mng_type == 2 && object_id != 0)
5298               (void) ThrowMagickException(exception,GetMagickModule(),
5299                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5300                 image->filename);
5301 
5302             if (object_id > MNG_MAX_OBJECTS)
5303               {
5304                 /*
5305                   Instead of using a warning we should allocate a larger
5306                   MngInfo structure and continue.
5307                 */
5308                 (void) ThrowMagickException(exception,GetMagickModule(),
5309                   CoderError,"object id too large","`%s'",image->filename);
5310                 object_id=MNG_MAX_OBJECTS;
5311               }
5312 
5313             if (mng_info->exists[object_id])
5314               if (mng_info->frozen[object_id])
5315                 {
5316                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5317                   (void) ThrowMagickException(exception,
5318                     GetMagickModule(),CoderError,
5319                     "DEFI cannot redefine a frozen MNG object","`%s'",
5320                     image->filename);
5321                   continue;
5322                 }
5323 
5324             mng_info->exists[object_id]=MagickTrue;
5325 
5326             if (length > 2)
5327               mng_info->invisible[object_id]=p[2];
5328 
5329             /*
5330               Extract object offset info.
5331             */
5332             if (length > 11)
5333               {
5334                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5335                     (p[5] << 16) | (p[6] << 8) | p[7]);
5336 
5337                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5338                     (p[9] << 16) | (p[10] << 8) | p[11]);
5339 
5340                 if (logging != MagickFalse)
5341                   {
5342                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5343                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5344                       object_id,(double) mng_info->x_off[object_id],
5345                       object_id,(double) mng_info->y_off[object_id]);
5346                   }
5347               }
5348 
5349             /*
5350               Extract object clipping info.
5351             */
5352             if (length > 27)
5353               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5354                 &p[12]);
5355 
5356             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5357             continue;
5358           }
5359         if (memcmp(type,mng_bKGD,4) == 0)
5360           {
5361             mng_info->have_global_bkgd=MagickFalse;
5362 
5363             if (length > 5)
5364               {
5365                 mng_info->mng_global_bkgd.red=
5366                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5367 
5368                 mng_info->mng_global_bkgd.green=
5369                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5370 
5371                 mng_info->mng_global_bkgd.blue=
5372                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5373 
5374                 mng_info->have_global_bkgd=MagickTrue;
5375               }
5376 
5377             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5378             continue;
5379           }
5380         if (memcmp(type,mng_BACK,4) == 0)
5381           {
5382 #if defined(MNG_INSERT_LAYERS)
5383             if (length > 6)
5384               mandatory_back=p[6];
5385 
5386             else
5387               mandatory_back=0;
5388 
5389             if (mandatory_back && length > 5)
5390               {
5391                 mng_background_color.red=
5392                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5393 
5394                 mng_background_color.green=
5395                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5396 
5397                 mng_background_color.blue=
5398                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5399 
5400                 mng_background_color.alpha=OpaqueAlpha;
5401               }
5402 
5403 #ifdef MNG_OBJECT_BUFFERS
5404             if (length > 8)
5405               mng_background_object=(p[7] << 8) | p[8];
5406 #endif
5407 #endif
5408             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5409             continue;
5410           }
5411 
5412         if (memcmp(type,mng_PLTE,4) == 0)
5413           {
5414             /* Read global PLTE.  */
5415 
5416             if (length && (length < 769))
5417               {
5418                 if (mng_info->global_plte == (png_colorp) NULL)
5419                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5420                     sizeof(*mng_info->global_plte));
5421 
5422                 for (i=0; i < (ssize_t) (length/3); i++)
5423                 {
5424                   mng_info->global_plte[i].red=p[3*i];
5425                   mng_info->global_plte[i].green=p[3*i+1];
5426                   mng_info->global_plte[i].blue=p[3*i+2];
5427                 }
5428 
5429                 mng_info->global_plte_length=(unsigned int) (length/3);
5430               }
5431 #ifdef MNG_LOOSE
5432             for ( ; i < 256; i++)
5433             {
5434               mng_info->global_plte[i].red=i;
5435               mng_info->global_plte[i].green=i;
5436               mng_info->global_plte[i].blue=i;
5437             }
5438 
5439             if (length != 0)
5440               mng_info->global_plte_length=256;
5441 #endif
5442             else
5443               mng_info->global_plte_length=0;
5444 
5445             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5446             continue;
5447           }
5448 
5449         if (memcmp(type,mng_tRNS,4) == 0)
5450           {
5451             /* read global tRNS */
5452 
5453             if (length > 0 && length < 257)
5454               for (i=0; i < (ssize_t) length; i++)
5455                 mng_info->global_trns[i]=p[i];
5456 
5457 #ifdef MNG_LOOSE
5458             for ( ; i < 256; i++)
5459               mng_info->global_trns[i]=255;
5460 #endif
5461             mng_info->global_trns_length=(unsigned int) length;
5462             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5463             continue;
5464           }
5465         if (memcmp(type,mng_gAMA,4) == 0)
5466           {
5467             if (length == 4)
5468               {
5469                 ssize_t
5470                   igamma;
5471 
5472                 igamma=mng_get_long(p);
5473                 mng_info->global_gamma=((float) igamma)*0.00001;
5474                 mng_info->have_global_gama=MagickTrue;
5475               }
5476 
5477             else
5478               mng_info->have_global_gama=MagickFalse;
5479 
5480             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5481             continue;
5482           }
5483 
5484         if (memcmp(type,mng_cHRM,4) == 0)
5485           {
5486             /* Read global cHRM */
5487 
5488             if (length == 32)
5489               {
5490                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5491                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5492                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5493                 mng_info->global_chrm.red_primary.y=0.00001*
5494                   mng_get_long(&p[12]);
5495                 mng_info->global_chrm.green_primary.x=0.00001*
5496                   mng_get_long(&p[16]);
5497                 mng_info->global_chrm.green_primary.y=0.00001*
5498                   mng_get_long(&p[20]);
5499                 mng_info->global_chrm.blue_primary.x=0.00001*
5500                   mng_get_long(&p[24]);
5501                 mng_info->global_chrm.blue_primary.y=0.00001*
5502                   mng_get_long(&p[28]);
5503                 mng_info->have_global_chrm=MagickTrue;
5504               }
5505             else
5506               mng_info->have_global_chrm=MagickFalse;
5507 
5508             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5509             continue;
5510           }
5511 
5512         if (memcmp(type,mng_sRGB,4) == 0)
5513           {
5514             /*
5515               Read global sRGB.
5516             */
5517             if (length != 0)
5518               {
5519                 mng_info->global_srgb_intent=
5520                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5521                 mng_info->have_global_srgb=MagickTrue;
5522               }
5523             else
5524               mng_info->have_global_srgb=MagickFalse;
5525 
5526             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5527             continue;
5528           }
5529 
5530         if (memcmp(type,mng_iCCP,4) == 0)
5531           {
5532             /* To do: */
5533 
5534             /*
5535               Read global iCCP.
5536             */
5537             if (length != 0)
5538               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5539 
5540             continue;
5541           }
5542 
5543         if (memcmp(type,mng_FRAM,4) == 0)
5544           {
5545             if (mng_type == 3)
5546               (void) ThrowMagickException(exception,GetMagickModule(),
5547                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5548                 image->filename);
5549 
5550             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5551               image->delay=frame_delay;
5552 
5553             frame_delay=default_frame_delay;
5554             frame_timeout=default_frame_timeout;
5555             fb=default_fb;
5556 
5557             if (length != 0)
5558               if (p[0])
5559                 mng_info->framing_mode=p[0];
5560 
5561             if (logging != MagickFalse)
5562               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5563                 "    Framing_mode=%d",mng_info->framing_mode);
5564 
5565             if (length > 6)
5566               {
5567                 /* Note the delay and frame clipping boundaries.  */
5568 
5569                 p++; /* framing mode */
5570 
5571                 while (*p && ((p-chunk) < (ssize_t) length))
5572                   p++;  /* frame name */
5573 
5574                 p++;  /* frame name terminator */
5575 
5576                 if ((p-chunk) < (ssize_t) (length-4))
5577                   {
5578                     int
5579                       change_delay,
5580                       change_timeout,
5581                       change_clipping;
5582 
5583                     change_delay=(*p++);
5584                     change_timeout=(*p++);
5585                     change_clipping=(*p++);
5586                     p++; /* change_sync */
5587 
5588                     if (change_delay)
5589                       {
5590                         frame_delay=1UL*image->ticks_per_second*
5591                           mng_get_long(p);
5592 
5593                         if (mng_info->ticks_per_second != 0)
5594                           frame_delay/=mng_info->ticks_per_second;
5595 
5596                         else
5597                           frame_delay=PNG_UINT_31_MAX;
5598 
5599                         if (change_delay == 2)
5600                           default_frame_delay=frame_delay;
5601 
5602                         p+=4;
5603 
5604                         if (logging != MagickFalse)
5605                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5606                             "    Framing_delay=%.20g",(double) frame_delay);
5607                       }
5608 
5609                     if (change_timeout)
5610                       {
5611                         frame_timeout=1UL*image->ticks_per_second*
5612                           mng_get_long(p);
5613 
5614                         if (mng_info->ticks_per_second != 0)
5615                           frame_timeout/=mng_info->ticks_per_second;
5616 
5617                         else
5618                           frame_timeout=PNG_UINT_31_MAX;
5619 
5620                         if (change_timeout == 2)
5621                           default_frame_timeout=frame_timeout;
5622 
5623                         p+=4;
5624 
5625                         if (logging != MagickFalse)
5626                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5627                             "    Framing_timeout=%.20g",(double) frame_timeout);
5628                       }
5629 
5630                     if (change_clipping)
5631                       {
5632                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5633                         p+=17;
5634                         previous_fb=fb;
5635 
5636                         if (logging != MagickFalse)
5637                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5638                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5639                             (double) fb.left,(double) fb.right,(double) fb.top,
5640                             (double) fb.bottom);
5641 
5642                         if (change_clipping == 2)
5643                           default_fb=fb;
5644                       }
5645                   }
5646               }
5647             mng_info->clip=fb;
5648             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5649 
5650             subframe_width=(size_t) (mng_info->clip.right
5651                -mng_info->clip.left);
5652 
5653             subframe_height=(size_t) (mng_info->clip.bottom
5654                -mng_info->clip.top);
5655             /*
5656               Insert a background layer behind the frame if framing_mode is 4.
5657             */
5658 #if defined(MNG_INSERT_LAYERS)
5659             if (logging != MagickFalse)
5660               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5661                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5662                 subframe_width,(double) subframe_height);
5663 
5664             if (insert_layers && (mng_info->framing_mode == 4) &&
5665                 (subframe_width) && (subframe_height))
5666               {
5667                 /* Allocate next image structure.  */
5668                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5669                   {
5670                     AcquireNextImage(image_info,image,exception);
5671 
5672                     if (GetNextImageInList(image) == (Image *) NULL)
5673                       {
5674                         image=DestroyImageList(image);
5675                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5676                         return((Image *) NULL);
5677                       }
5678 
5679                     image=SyncNextImageInList(image);
5680                   }
5681 
5682                 mng_info->image=image;
5683 
5684                 if (term_chunk_found)
5685                   {
5686                     image->start_loop=MagickTrue;
5687                     image->iterations=mng_iterations;
5688                     term_chunk_found=MagickFalse;
5689                   }
5690 
5691                 else
5692                     image->start_loop=MagickFalse;
5693 
5694                 image->columns=subframe_width;
5695                 image->rows=subframe_height;
5696                 image->page.width=subframe_width;
5697                 image->page.height=subframe_height;
5698                 image->page.x=mng_info->clip.left;
5699                 image->page.y=mng_info->clip.top;
5700                 image->background_color=mng_background_color;
5701                 image->alpha_trait=UndefinedPixelTrait;
5702                 image->delay=0;
5703                 (void) SetImageBackgroundColor(image,exception);
5704 
5705                 if (logging != MagickFalse)
5706                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5707                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5708                     (double) mng_info->clip.left,(double) mng_info->clip.right,
5709                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5710               }
5711 #endif
5712             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5713             continue;
5714           }
5715 
5716         if (memcmp(type,mng_CLIP,4) == 0)
5717           {
5718             unsigned int
5719               first_object,
5720               last_object;
5721 
5722             /*
5723               Read CLIP.
5724             */
5725             if (length > 3)
5726               {
5727                 first_object=(p[0] << 8) | p[1];
5728                 last_object=(p[2] << 8) | p[3];
5729                 p+=4;
5730 
5731                 for (i=(int) first_object; i <= (int) last_object; i++)
5732                 {
5733                   if (mng_info->exists[i] && !mng_info->frozen[i])
5734                     {
5735                       MngBox
5736                         box;
5737 
5738                       box=mng_info->object_clip[i];
5739                       if ((p-chunk) < (ssize_t) (length-17))
5740                         mng_info->object_clip[i]=
5741                            mng_read_box(box,(char) p[0],&p[1]);
5742                     }
5743                 }
5744 
5745               }
5746             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5747             continue;
5748           }
5749 
5750         if (memcmp(type,mng_SAVE,4) == 0)
5751           {
5752             for (i=1; i < MNG_MAX_OBJECTS; i++)
5753               if (mng_info->exists[i])
5754                 {
5755                  mng_info->frozen[i]=MagickTrue;
5756 #ifdef MNG_OBJECT_BUFFERS
5757                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5758                     mng_info->ob[i]->frozen=MagickTrue;
5759 #endif
5760                 }
5761 
5762             if (length != 0)
5763               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5764 
5765             continue;
5766           }
5767 
5768         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5769           {
5770             /* Read DISC or SEEK.  */
5771 
5772             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5773               {
5774                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5775                   MngInfoDiscardObject(mng_info,i);
5776               }
5777 
5778             else
5779               {
5780                 register ssize_t
5781                   j;
5782 
5783                 for (j=1; j < (ssize_t) length; j+=2)
5784                 {
5785                   i=p[j-1] << 8 | p[j];
5786                   MngInfoDiscardObject(mng_info,i);
5787                 }
5788               }
5789 
5790             if (length != 0)
5791               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5792 
5793             continue;
5794           }
5795 
5796         if (memcmp(type,mng_MOVE,4) == 0)
5797           {
5798             size_t
5799               first_object,
5800               last_object;
5801 
5802             /* read MOVE */
5803 
5804             if (length > 3)
5805             {
5806               first_object=(p[0] << 8) | p[1];
5807               last_object=(p[2] << 8) | p[3];
5808               p+=4;
5809 
5810               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5811               {
5812                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5813                     (p-chunk) < (ssize_t) (length-8))
5814                   {
5815                     MngPair
5816                       new_pair;
5817 
5818                     MngPair
5819                       old_pair;
5820 
5821                     old_pair.a=mng_info->x_off[i];
5822                     old_pair.b=mng_info->y_off[i];
5823                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
5824                     mng_info->x_off[i]=new_pair.a;
5825                     mng_info->y_off[i]=new_pair.b;
5826                   }
5827               }
5828             }
5829 
5830             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5831             continue;
5832           }
5833 
5834         if (memcmp(type,mng_LOOP,4) == 0)
5835           {
5836             ssize_t loop_iters=1;
5837             if (length > 4)
5838               {
5839                 loop_level=chunk[0];
5840                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
5841 
5842                 /* Record starting point.  */
5843                 loop_iters=mng_get_long(&chunk[1]);
5844 
5845                 if (logging != MagickFalse)
5846                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5847                     "  LOOP level %.20g has %.20g iterations ",
5848                     (double) loop_level, (double) loop_iters);
5849 
5850                 if (loop_iters == 0)
5851                   skipping_loop=loop_level;
5852 
5853                 else
5854                   {
5855                     mng_info->loop_jump[loop_level]=TellBlob(image);
5856                     mng_info->loop_count[loop_level]=loop_iters;
5857                   }
5858 
5859                 mng_info->loop_iteration[loop_level]=0;
5860               }
5861             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5862             continue;
5863           }
5864 
5865         if (memcmp(type,mng_ENDL,4) == 0)
5866           {
5867             if (length > 0)
5868               {
5869                 loop_level=chunk[0];
5870 
5871                 if (skipping_loop > 0)
5872                   {
5873                     if (skipping_loop == loop_level)
5874                       {
5875                         /*
5876                           Found end of zero-iteration loop.
5877                         */
5878                         skipping_loop=(-1);
5879                         mng_info->loop_active[loop_level]=0;
5880                       }
5881                   }
5882 
5883                 else
5884                   {
5885                     if (mng_info->loop_active[loop_level] == 1)
5886                       {
5887                         mng_info->loop_count[loop_level]--;
5888                         mng_info->loop_iteration[loop_level]++;
5889 
5890                         if (logging != MagickFalse)
5891                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5892                           "  ENDL: LOOP level %.20g has %.20g remaining iters ",
5893                             (double) loop_level,(double)
5894                             mng_info->loop_count[loop_level]);
5895 
5896                         if (mng_info->loop_count[loop_level] != 0)
5897                           {
5898                             offset=
5899                               SeekBlob(image,mng_info->loop_jump[loop_level],
5900                               SEEK_SET);
5901 
5902                             if (offset < 0)
5903                               ThrowReaderException(CorruptImageError,
5904                                 "ImproperImageHeader");
5905                           }
5906 
5907                         else
5908                           {
5909                             short
5910                               last_level;
5911 
5912                             /*
5913                               Finished loop.
5914                             */
5915                             mng_info->loop_active[loop_level]=0;
5916                             last_level=(-1);
5917                             for (i=0; i < loop_level; i++)
5918                               if (mng_info->loop_active[i] == 1)
5919                                 last_level=(short) i;
5920                             loop_level=last_level;
5921                           }
5922                       }
5923                   }
5924               }
5925 
5926             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5927             continue;
5928           }
5929 
5930         if (memcmp(type,mng_CLON,4) == 0)
5931           {
5932             if (mng_info->clon_warning == 0)
5933               (void) ThrowMagickException(exception,GetMagickModule(),
5934                 CoderError,"CLON is not implemented yet","`%s'",
5935                 image->filename);
5936 
5937             mng_info->clon_warning++;
5938           }
5939 
5940         if (memcmp(type,mng_MAGN,4) == 0)
5941           {
5942             png_uint_16
5943               magn_first,
5944               magn_last,
5945               magn_mb,
5946               magn_ml,
5947               magn_mr,
5948               magn_mt,
5949               magn_mx,
5950               magn_my,
5951               magn_methx,
5952               magn_methy;
5953 
5954             if (length > 1)
5955               magn_first=(p[0] << 8) | p[1];
5956 
5957             else
5958               magn_first=0;
5959 
5960             if (length > 3)
5961               magn_last=(p[2] << 8) | p[3];
5962 
5963             else
5964               magn_last=magn_first;
5965 #ifndef MNG_OBJECT_BUFFERS
5966             if (magn_first || magn_last)
5967               if (mng_info->magn_warning == 0)
5968                 {
5969                   (void) ThrowMagickException(exception,
5970                      GetMagickModule(),CoderError,
5971                      "MAGN is not implemented yet for nonzero objects",
5972                      "`%s'",image->filename);
5973 
5974                    mng_info->magn_warning++;
5975                 }
5976 #endif
5977             if (length > 4)
5978               magn_methx=p[4];
5979 
5980             else
5981               magn_methx=0;
5982 
5983             if (length > 6)
5984               magn_mx=(p[5] << 8) | p[6];
5985 
5986             else
5987               magn_mx=1;
5988 
5989             if (magn_mx == 0)
5990               magn_mx=1;
5991 
5992             if (length > 8)
5993               magn_my=(p[7] << 8) | p[8];
5994 
5995             else
5996               magn_my=magn_mx;
5997 
5998             if (magn_my == 0)
5999               magn_my=1;
6000 
6001             if (length > 10)
6002               magn_ml=(p[9] << 8) | p[10];
6003 
6004             else
6005               magn_ml=magn_mx;
6006 
6007             if (magn_ml == 0)
6008               magn_ml=1;
6009 
6010             if (length > 12)
6011               magn_mr=(p[11] << 8) | p[12];
6012 
6013             else
6014               magn_mr=magn_mx;
6015 
6016             if (magn_mr == 0)
6017               magn_mr=1;
6018 
6019             if (length > 14)
6020               magn_mt=(p[13] << 8) | p[14];
6021 
6022             else
6023               magn_mt=magn_my;
6024 
6025             if (magn_mt == 0)
6026               magn_mt=1;
6027 
6028             if (length > 16)
6029               magn_mb=(p[15] << 8) | p[16];
6030 
6031             else
6032               magn_mb=magn_my;
6033 
6034             if (magn_mb == 0)
6035               magn_mb=1;
6036 
6037             if (length > 17)
6038               magn_methy=p[17];
6039 
6040             else
6041               magn_methy=magn_methx;
6042 
6043 
6044             if (magn_methx > 5 || magn_methy > 5)
6045               if (mng_info->magn_warning == 0)
6046                 {
6047                   (void) ThrowMagickException(exception,
6048                      GetMagickModule(),CoderError,
6049                      "Unknown MAGN method in MNG datastream","`%s'",
6050                      image->filename);
6051 
6052                    mng_info->magn_warning++;
6053                 }
6054 #ifdef MNG_OBJECT_BUFFERS
6055           /* Magnify existing objects in the range magn_first to magn_last */
6056 #endif
6057             if (magn_first == 0 || magn_last == 0)
6058               {
6059                 /* Save the magnification factors for object 0 */
6060                 mng_info->magn_mb=magn_mb;
6061                 mng_info->magn_ml=magn_ml;
6062                 mng_info->magn_mr=magn_mr;
6063                 mng_info->magn_mt=magn_mt;
6064                 mng_info->magn_mx=magn_mx;
6065                 mng_info->magn_my=magn_my;
6066                 mng_info->magn_methx=magn_methx;
6067                 mng_info->magn_methy=magn_methy;
6068               }
6069           }
6070 
6071         if (memcmp(type,mng_PAST,4) == 0)
6072           {
6073             if (mng_info->past_warning == 0)
6074               (void) ThrowMagickException(exception,GetMagickModule(),
6075                 CoderError,"PAST is not implemented yet","`%s'",
6076                 image->filename);
6077 
6078             mng_info->past_warning++;
6079           }
6080 
6081         if (memcmp(type,mng_SHOW,4) == 0)
6082           {
6083             if (mng_info->show_warning == 0)
6084               (void) ThrowMagickException(exception,GetMagickModule(),
6085                 CoderError,"SHOW is not implemented yet","`%s'",
6086                 image->filename);
6087 
6088             mng_info->show_warning++;
6089           }
6090 
6091         if (memcmp(type,mng_sBIT,4) == 0)
6092           {
6093             if (length < 4)
6094               mng_info->have_global_sbit=MagickFalse;
6095 
6096             else
6097               {
6098                 mng_info->global_sbit.gray=p[0];
6099                 mng_info->global_sbit.red=p[0];
6100                 mng_info->global_sbit.green=p[1];
6101                 mng_info->global_sbit.blue=p[2];
6102                 mng_info->global_sbit.alpha=p[3];
6103                 mng_info->have_global_sbit=MagickTrue;
6104              }
6105           }
6106         if (memcmp(type,mng_pHYs,4) == 0)
6107           {
6108             if (length > 8)
6109               {
6110                 mng_info->global_x_pixels_per_unit=
6111                     (size_t) mng_get_long(p);
6112                 mng_info->global_y_pixels_per_unit=
6113                     (size_t) mng_get_long(&p[4]);
6114                 mng_info->global_phys_unit_type=p[8];
6115                 mng_info->have_global_phys=MagickTrue;
6116               }
6117 
6118             else
6119               mng_info->have_global_phys=MagickFalse;
6120           }
6121         if (memcmp(type,mng_pHYg,4) == 0)
6122           {
6123             if (mng_info->phyg_warning == 0)
6124               (void) ThrowMagickException(exception,GetMagickModule(),
6125                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6126 
6127             mng_info->phyg_warning++;
6128           }
6129         if (memcmp(type,mng_BASI,4) == 0)
6130           {
6131             skip_to_iend=MagickTrue;
6132 
6133             if (mng_info->basi_warning == 0)
6134               (void) ThrowMagickException(exception,GetMagickModule(),
6135                 CoderError,"BASI is not implemented yet","`%s'",
6136                 image->filename);
6137 
6138             mng_info->basi_warning++;
6139 #ifdef MNG_BASI_SUPPORTED
6140             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6141                (p[2] << 8) | p[3]);
6142             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6143                (p[6] << 8) | p[7]);
6144             basi_color_type=p[8];
6145             basi_compression_method=p[9];
6146             basi_filter_type=p[10];
6147             basi_interlace_method=p[11];
6148             if (length > 11)
6149               basi_red=(p[12] << 8) & p[13];
6150 
6151             else
6152               basi_red=0;
6153 
6154             if (length > 13)
6155               basi_green=(p[14] << 8) & p[15];
6156 
6157             else
6158               basi_green=0;
6159 
6160             if (length > 15)
6161               basi_blue=(p[16] << 8) & p[17];
6162 
6163             else
6164               basi_blue=0;
6165 
6166             if (length > 17)
6167               basi_alpha=(p[18] << 8) & p[19];
6168 
6169             else
6170               {
6171                 if (basi_sample_depth == 16)
6172                   basi_alpha=65535L;
6173                 else
6174                   basi_alpha=255;
6175               }
6176 
6177             if (length > 19)
6178               basi_viewable=p[20];
6179 
6180             else
6181               basi_viewable=0;
6182 
6183 #endif
6184             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6185             continue;
6186           }
6187 
6188         if (memcmp(type,mng_IHDR,4)
6189 #if defined(JNG_SUPPORTED)
6190             && memcmp(type,mng_JHDR,4)
6191 #endif
6192             )
6193           {
6194             /* Not an IHDR or JHDR chunk */
6195             if (length != 0)
6196               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6197 
6198             continue;
6199           }
6200 /* Process IHDR */
6201         if (logging != MagickFalse)
6202           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6203             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6204 
6205         mng_info->exists[object_id]=MagickTrue;
6206         mng_info->viewable[object_id]=MagickTrue;
6207 
6208         if (mng_info->invisible[object_id])
6209           {
6210             if (logging != MagickFalse)
6211               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6212                 "  Skipping invisible object");
6213 
6214             skip_to_iend=MagickTrue;
6215             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6216             continue;
6217           }
6218 #if defined(MNG_INSERT_LAYERS)
6219         if (length < 8)
6220           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6221 
6222         image_width=(size_t) mng_get_long(p);
6223         image_height=(size_t) mng_get_long(&p[4]);
6224 #endif
6225         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6226 
6227         /*
6228           Insert a transparent background layer behind the entire animation
6229           if it is not full screen.
6230         */
6231 #if defined(MNG_INSERT_LAYERS)
6232         if (insert_layers && mng_type && first_mng_object)
6233           {
6234             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6235                 (image_width < mng_info->mng_width) ||
6236                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6237                 (image_height < mng_info->mng_height) ||
6238                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6239               {
6240                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6241                   {
6242                     /*
6243                       Allocate next image structure.
6244                     */
6245                     AcquireNextImage(image_info,image,exception);
6246 
6247                     if (GetNextImageInList(image) == (Image *) NULL)
6248                       {
6249                         image=DestroyImageList(image);
6250                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6251                         return((Image *) NULL);
6252                       }
6253 
6254                     image=SyncNextImageInList(image);
6255                   }
6256                 mng_info->image=image;
6257 
6258                 if (term_chunk_found)
6259                   {
6260                     image->start_loop=MagickTrue;
6261                     image->iterations=mng_iterations;
6262                     term_chunk_found=MagickFalse;
6263                   }
6264 
6265                 else
6266                     image->start_loop=MagickFalse;
6267 
6268                 /* Make a background rectangle.  */
6269 
6270                 image->delay=0;
6271                 image->columns=mng_info->mng_width;
6272                 image->rows=mng_info->mng_height;
6273                 image->page.width=mng_info->mng_width;
6274                 image->page.height=mng_info->mng_height;
6275                 image->page.x=0;
6276                 image->page.y=0;
6277                 image->background_color=mng_background_color;
6278                 (void) SetImageBackgroundColor(image,exception);
6279                 if (logging != MagickFalse)
6280                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6281                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6282                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6283               }
6284           }
6285         /*
6286           Insert a background layer behind the upcoming image if
6287           framing_mode is 3, and we haven't already inserted one.
6288         */
6289         if (insert_layers && (mng_info->framing_mode == 3) &&
6290                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6291                 (simplicity & 0x08)))
6292           {
6293             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6294             {
6295               /*
6296                 Allocate next image structure.
6297               */
6298               AcquireNextImage(image_info,image,exception);
6299 
6300               if (GetNextImageInList(image) == (Image *) NULL)
6301                 {
6302                   image=DestroyImageList(image);
6303                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6304                   return((Image *) NULL);
6305                 }
6306 
6307               image=SyncNextImageInList(image);
6308             }
6309 
6310             mng_info->image=image;
6311 
6312             if (term_chunk_found)
6313               {
6314                 image->start_loop=MagickTrue;
6315                 image->iterations=mng_iterations;
6316                 term_chunk_found=MagickFalse;
6317               }
6318 
6319             else
6320                 image->start_loop=MagickFalse;
6321 
6322             image->delay=0;
6323             image->columns=subframe_width;
6324             image->rows=subframe_height;
6325             image->page.width=subframe_width;
6326             image->page.height=subframe_height;
6327             image->page.x=mng_info->clip.left;
6328             image->page.y=mng_info->clip.top;
6329             image->background_color=mng_background_color;
6330             image->alpha_trait=UndefinedPixelTrait;
6331             (void) SetImageBackgroundColor(image,exception);
6332 
6333             if (logging != MagickFalse)
6334               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6335                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6336                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6337                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6338           }
6339 #endif /* MNG_INSERT_LAYERS */
6340         first_mng_object=MagickFalse;
6341 
6342         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6343           {
6344             /*
6345               Allocate next image structure.
6346             */
6347             AcquireNextImage(image_info,image,exception);
6348 
6349             if (GetNextImageInList(image) == (Image *) NULL)
6350               {
6351                 image=DestroyImageList(image);
6352                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6353                 return((Image *) NULL);
6354               }
6355 
6356             image=SyncNextImageInList(image);
6357           }
6358         mng_info->image=image;
6359         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6360           GetBlobSize(image));
6361 
6362         if (status == MagickFalse)
6363           break;
6364 
6365         if (term_chunk_found)
6366           {
6367             image->start_loop=MagickTrue;
6368             term_chunk_found=MagickFalse;
6369           }
6370 
6371         else
6372             image->start_loop=MagickFalse;
6373 
6374         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6375           {
6376             image->delay=frame_delay;
6377             frame_delay=default_frame_delay;
6378           }
6379 
6380         else
6381           image->delay=0;
6382 
6383         image->page.width=mng_info->mng_width;
6384         image->page.height=mng_info->mng_height;
6385         image->page.x=mng_info->x_off[object_id];
6386         image->page.y=mng_info->y_off[object_id];
6387         image->iterations=mng_iterations;
6388 
6389         /*
6390           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6391         */
6392 
6393         if (logging != MagickFalse)
6394           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6395             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6396             type[2],type[3]);
6397 
6398         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6399 
6400         if (offset < 0)
6401           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6402       }
6403 
6404     mng_info->image=image;
6405     mng_info->mng_type=mng_type;
6406     mng_info->object_id=object_id;
6407 
6408     if (memcmp(type,mng_IHDR,4) == 0)
6409       image=ReadOnePNGImage(mng_info,image_info,exception);
6410 
6411 #if defined(JNG_SUPPORTED)
6412     else
6413       image=ReadOneJNGImage(mng_info,image_info,exception);
6414 #endif
6415 
6416     if (image == (Image *) NULL)
6417       {
6418         if (logging != MagickFalse)
6419           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6420             "exit ReadJNGImage() with error");
6421 
6422         MngInfoFreeStruct(mng_info,&have_mng_structure);
6423         return((Image *) NULL);
6424       }
6425 
6426     if (image->columns == 0 || image->rows == 0)
6427       {
6428         (void) CloseBlob(image);
6429         image=DestroyImageList(image);
6430         MngInfoFreeStruct(mng_info,&have_mng_structure);
6431         return((Image *) NULL);
6432       }
6433 
6434     mng_info->image=image;
6435 
6436     if (mng_type)
6437       {
6438         MngBox
6439           crop_box;
6440 
6441         if (mng_info->magn_methx || mng_info->magn_methy)
6442           {
6443             png_uint_32
6444                magnified_height,
6445                magnified_width;
6446 
6447             if (logging != MagickFalse)
6448               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6449                 "  Processing MNG MAGN chunk");
6450 
6451             if (mng_info->magn_methx == 1)
6452               {
6453                 magnified_width=mng_info->magn_ml;
6454 
6455                 if (image->columns > 1)
6456                    magnified_width += mng_info->magn_mr;
6457 
6458                 if (image->columns > 2)
6459                    magnified_width += (png_uint_32)
6460                       ((image->columns-2)*(mng_info->magn_mx));
6461               }
6462 
6463             else
6464               {
6465                 magnified_width=(png_uint_32) image->columns;
6466 
6467                 if (image->columns > 1)
6468                    magnified_width += mng_info->magn_ml-1;
6469 
6470                 if (image->columns > 2)
6471                    magnified_width += mng_info->magn_mr-1;
6472 
6473                 if (image->columns > 3)
6474                    magnified_width += (png_uint_32)
6475                       ((image->columns-3)*(mng_info->magn_mx-1));
6476               }
6477 
6478             if (mng_info->magn_methy == 1)
6479               {
6480                 magnified_height=mng_info->magn_mt;
6481 
6482                 if (image->rows > 1)
6483                    magnified_height += mng_info->magn_mb;
6484 
6485                 if (image->rows > 2)
6486                    magnified_height += (png_uint_32)
6487                       ((image->rows-2)*(mng_info->magn_my));
6488               }
6489 
6490             else
6491               {
6492                 magnified_height=(png_uint_32) image->rows;
6493 
6494                 if (image->rows > 1)
6495                    magnified_height += mng_info->magn_mt-1;
6496 
6497                 if (image->rows > 2)
6498                    magnified_height += mng_info->magn_mb-1;
6499 
6500                 if (image->rows > 3)
6501                    magnified_height += (png_uint_32)
6502                       ((image->rows-3)*(mng_info->magn_my-1));
6503               }
6504 
6505             if (magnified_height > image->rows ||
6506                 magnified_width > image->columns)
6507               {
6508                 Image
6509                   *large_image;
6510 
6511                 int
6512                   yy;
6513 
6514                 Quantum
6515                   *next,
6516                   *prev;
6517 
6518                 png_uint_16
6519                   magn_methx,
6520                   magn_methy;
6521 
6522                 ssize_t
6523                   m,
6524                   y;
6525 
6526                 register Quantum
6527                   *n,
6528                   *q;
6529 
6530                 register ssize_t
6531                   x;
6532 
6533                 /* Allocate next image structure.  */
6534 
6535                 if (logging != MagickFalse)
6536                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6537                     "    Allocate magnified image");
6538 
6539                 AcquireNextImage(image_info,image,exception);
6540 
6541                 if (GetNextImageInList(image) == (Image *) NULL)
6542                   {
6543                     image=DestroyImageList(image);
6544                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6545                     return((Image *) NULL);
6546                   }
6547 
6548                 large_image=SyncNextImageInList(image);
6549 
6550                 large_image->columns=magnified_width;
6551                 large_image->rows=magnified_height;
6552 
6553                 magn_methx=mng_info->magn_methx;
6554                 magn_methy=mng_info->magn_methy;
6555 
6556 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6557 #define QM unsigned short
6558                 if (magn_methx != 1 || magn_methy != 1)
6559                   {
6560                   /*
6561                      Scale pixels to unsigned shorts to prevent
6562                      overflow of intermediate values of interpolations
6563                   */
6564                      for (y=0; y < (ssize_t) image->rows; y++)
6565                      {
6566                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6567                           exception);
6568 
6569                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6570                        {
6571                           SetPixelRed(image,ScaleQuantumToShort(
6572                             GetPixelRed(image,q)),q);
6573                           SetPixelGreen(image,ScaleQuantumToShort(
6574                             GetPixelGreen(image,q)),q);
6575                           SetPixelBlue(image,ScaleQuantumToShort(
6576                             GetPixelBlue(image,q)),q);
6577                           SetPixelAlpha(image,ScaleQuantumToShort(
6578                             GetPixelAlpha(image,q)),q);
6579                           q+=GetPixelChannels(image);
6580                        }
6581 
6582                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6583                          break;
6584                      }
6585                   }
6586 #else
6587 #define QM Quantum
6588 #endif
6589 
6590                 if (image->alpha_trait != UndefinedPixelTrait)
6591                    (void) SetImageBackgroundColor(large_image,exception);
6592 
6593                 else
6594                   {
6595                     large_image->background_color.alpha=OpaqueAlpha;
6596                     (void) SetImageBackgroundColor(large_image,exception);
6597 
6598                     if (magn_methx == 4)
6599                       magn_methx=2;
6600 
6601                     if (magn_methx == 5)
6602                       magn_methx=3;
6603 
6604                     if (magn_methy == 4)
6605                       magn_methy=2;
6606 
6607                     if (magn_methy == 5)
6608                       magn_methy=3;
6609                   }
6610 
6611                 /* magnify the rows into the right side of the large image */
6612 
6613                 if (logging != MagickFalse)
6614                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6615                     "    Magnify the rows to %.20g",(double) large_image->rows);
6616                 m=(ssize_t) mng_info->magn_mt;
6617                 yy=0;
6618                 length=(size_t) GetPixelChannels(image)*image->columns;
6619                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6620                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6621 
6622                 if ((prev == (Quantum *) NULL) ||
6623                     (next == (Quantum *) NULL))
6624                   {
6625                      image=DestroyImageList(image);
6626                      MngInfoFreeStruct(mng_info,&have_mng_structure);
6627                      ThrowReaderException(ResourceLimitError,
6628                        "MemoryAllocationFailed");
6629                   }
6630 
6631                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6632                 (void) CopyMagickMemory(next,n,length);
6633 
6634                 for (y=0; y < (ssize_t) image->rows; y++)
6635                 {
6636                   if (y == 0)
6637                     m=(ssize_t) mng_info->magn_mt;
6638 
6639                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6640                     m=(ssize_t) mng_info->magn_mb;
6641 
6642                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6643                     m=(ssize_t) mng_info->magn_mb;
6644 
6645                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6646                     m=1;
6647 
6648                   else
6649                     m=(ssize_t) mng_info->magn_my;
6650 
6651                   n=prev;
6652                   prev=next;
6653                   next=n;
6654 
6655                   if (y < (ssize_t) image->rows-1)
6656                     {
6657                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6658                           exception);
6659                       (void) CopyMagickMemory(next,n,length);
6660                     }
6661 
6662                   for (i=0; i < m; i++, yy++)
6663                   {
6664                     register Quantum
6665                       *pixels;
6666 
6667                     assert(yy < (ssize_t) large_image->rows);
6668                     pixels=prev;
6669                     n=next;
6670                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6671                       1,exception);
6672                     q+=(large_image->columns-image->columns)*
6673                       GetPixelChannels(large_image);
6674 
6675                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6676                     {
6677                       /* To do: get color as function of indexes[x] */
6678                       /*
6679                       if (image->storage_class == PseudoClass)
6680                         {
6681                         }
6682                       */
6683 
6684                       if (magn_methy <= 1)
6685                         {
6686                           /* replicate previous */
6687                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6688                           SetPixelGreen(large_image,GetPixelGreen(image,
6689                              pixels),q);
6690                           SetPixelBlue(large_image,GetPixelBlue(image,
6691                              pixels),q);
6692                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6693                              pixels),q);
6694                         }
6695 
6696                       else if (magn_methy == 2 || magn_methy == 4)
6697                         {
6698                           if (i == 0)
6699                             {
6700                               SetPixelRed(large_image,GetPixelRed(image,
6701                                  pixels),q);
6702                               SetPixelGreen(large_image,GetPixelGreen(image,
6703                                  pixels),q);
6704                               SetPixelBlue(large_image,GetPixelBlue(image,
6705                                  pixels),q);
6706                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6707                                  pixels),q);
6708                             }
6709 
6710                           else
6711                             {
6712                               /* Interpolate */
6713                               SetPixelRed(large_image,((QM) (((ssize_t)
6714                                  (2*i*(GetPixelRed(image,n)
6715                                  -GetPixelRed(image,pixels)+m))/
6716                                  ((ssize_t) (m*2))
6717                                  +GetPixelRed(image,pixels)))),q);
6718                               SetPixelGreen(large_image,((QM) (((ssize_t)
6719                                  (2*i*(GetPixelGreen(image,n)
6720                                  -GetPixelGreen(image,pixels)+m))/
6721                                  ((ssize_t) (m*2))
6722                                  +GetPixelGreen(image,pixels)))),q);
6723                               SetPixelBlue(large_image,((QM) (((ssize_t)
6724                                  (2*i*(GetPixelBlue(image,n)
6725                                  -GetPixelBlue(image,pixels)+m))/
6726                                  ((ssize_t) (m*2))
6727                                  +GetPixelBlue(image,pixels)))),q);
6728 
6729                               if (image->alpha_trait != UndefinedPixelTrait)
6730                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6731                                     (2*i*(GetPixelAlpha(image,n)
6732                                     -GetPixelAlpha(image,pixels)+m))
6733                                     /((ssize_t) (m*2))+
6734                                    GetPixelAlpha(image,pixels)))),q);
6735                             }
6736 
6737                           if (magn_methy == 4)
6738                             {
6739                               /* Replicate nearest */
6740                               if (i <= ((m+1) << 1))
6741                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6742                                     pixels),q);
6743                               else
6744                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6745                                     n),q);
6746                             }
6747                         }
6748 
6749                       else /* if (magn_methy == 3 || magn_methy == 5) */
6750                         {
6751                           /* Replicate nearest */
6752                           if (i <= ((m+1) << 1))
6753                           {
6754                              SetPixelRed(large_image,GetPixelRed(image,
6755                                     pixels),q);
6756                              SetPixelGreen(large_image,GetPixelGreen(image,
6757                                     pixels),q);
6758                              SetPixelBlue(large_image,GetPixelBlue(image,
6759                                     pixels),q);
6760                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6761                                     pixels),q);
6762                           }
6763 
6764                           else
6765                           {
6766                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6767                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6768                                     q);
6769                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6770                                     q);
6771                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6772                                     q);
6773                           }
6774 
6775                           if (magn_methy == 5)
6776                             {
6777                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6778                                  (GetPixelAlpha(image,n)
6779                                  -GetPixelAlpha(image,pixels))
6780                                  +m))/((ssize_t) (m*2))
6781                                  +GetPixelAlpha(image,pixels)),q);
6782                             }
6783                         }
6784                       n+=GetPixelChannels(image);
6785                       q+=GetPixelChannels(large_image);
6786                       pixels+=GetPixelChannels(image);
6787                     } /* x */
6788 
6789                     if (SyncAuthenticPixels(large_image,exception) == 0)
6790                       break;
6791 
6792                   } /* i */
6793                 } /* y */
6794 
6795                 prev=(Quantum *) RelinquishMagickMemory(prev);
6796                 next=(Quantum *) RelinquishMagickMemory(next);
6797 
6798                 length=image->columns;
6799 
6800                 if (logging != MagickFalse)
6801                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6802                     "    Delete original image");
6803 
6804                 DeleteImageFromList(&image);
6805 
6806                 image=large_image;
6807 
6808                 mng_info->image=image;
6809 
6810                 /* magnify the columns */
6811                 if (logging != MagickFalse)
6812                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6813                     "    Magnify the columns to %.20g",(double) image->columns);
6814 
6815                 for (y=0; y < (ssize_t) image->rows; y++)
6816                 {
6817                   register Quantum
6818                     *pixels;
6819 
6820                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6821                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6822                   n=pixels+GetPixelChannels(image);
6823 
6824                   for (x=(ssize_t) (image->columns-length);
6825                     x < (ssize_t) image->columns; x++)
6826                   {
6827                     /* To do: Rewrite using Get/Set***PixelChannel() */
6828 
6829                     if (x == (ssize_t) (image->columns-length))
6830                       m=(ssize_t) mng_info->magn_ml;
6831 
6832                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6833                       m=(ssize_t) mng_info->magn_mr;
6834 
6835                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6836                       m=(ssize_t) mng_info->magn_mr;
6837 
6838                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6839                       m=1;
6840 
6841                     else
6842                       m=(ssize_t) mng_info->magn_mx;
6843 
6844                     for (i=0; i < m; i++)
6845                     {
6846                       if (magn_methx <= 1)
6847                         {
6848                           /* replicate previous */
6849                           SetPixelRed(image,GetPixelRed(image,pixels),q);
6850                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6851                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6852                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6853                         }
6854 
6855                       else if (magn_methx == 2 || magn_methx == 4)
6856                         {
6857                           if (i == 0)
6858                           {
6859                             SetPixelRed(image,GetPixelRed(image,pixels),q);
6860                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6861                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6862                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6863                           }
6864 
6865                           /* To do: Rewrite using Get/Set***PixelChannel() */
6866                           else
6867                             {
6868                               /* Interpolate */
6869                               SetPixelRed(image,(QM) ((2*i*(
6870                                  GetPixelRed(image,n)
6871                                  -GetPixelRed(image,pixels))+m)
6872                                  /((ssize_t) (m*2))+
6873                                  GetPixelRed(image,pixels)),q);
6874 
6875                               SetPixelGreen(image,(QM) ((2*i*(
6876                                  GetPixelGreen(image,n)
6877                                  -GetPixelGreen(image,pixels))+m)
6878                                  /((ssize_t) (m*2))+
6879                                  GetPixelGreen(image,pixels)),q);
6880 
6881                               SetPixelBlue(image,(QM) ((2*i*(
6882                                  GetPixelBlue(image,n)
6883                                  -GetPixelBlue(image,pixels))+m)
6884                                  /((ssize_t) (m*2))+
6885                                  GetPixelBlue(image,pixels)),q);
6886                               if (image->alpha_trait != UndefinedPixelTrait)
6887                                  SetPixelAlpha(image,(QM) ((2*i*(
6888                                    GetPixelAlpha(image,n)
6889                                    -GetPixelAlpha(image,pixels))+m)
6890                                    /((ssize_t) (m*2))+
6891                                    GetPixelAlpha(image,pixels)),q);
6892                             }
6893 
6894                           if (magn_methx == 4)
6895                             {
6896                               /* Replicate nearest */
6897                               if (i <= ((m+1) << 1))
6898                               {
6899                                  SetPixelAlpha(image,
6900                                    GetPixelAlpha(image,pixels)+0,q);
6901                               }
6902                               else
6903                               {
6904                                  SetPixelAlpha(image,
6905                                    GetPixelAlpha(image,n)+0,q);
6906                               }
6907                             }
6908                         }
6909 
6910                       else /* if (magn_methx == 3 || magn_methx == 5) */
6911                         {
6912                           /* Replicate nearest */
6913                           if (i <= ((m+1) << 1))
6914                           {
6915                              SetPixelRed(image,GetPixelRed(image,pixels),q);
6916                              SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6917                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6918                              SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6919                           }
6920 
6921                           else
6922                           {
6923                              SetPixelRed(image,GetPixelRed(image,n),q);
6924                              SetPixelGreen(image,GetPixelGreen(image,n),q);
6925                              SetPixelBlue(image,GetPixelBlue(image,n),q);
6926                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6927                           }
6928 
6929                           if (magn_methx == 5)
6930                             {
6931                               /* Interpolate */
6932                               SetPixelAlpha(image,
6933                                  (QM) ((2*i*( GetPixelAlpha(image,n)
6934                                  -GetPixelAlpha(image,pixels))+m)/
6935                                  ((ssize_t) (m*2))
6936                                  +GetPixelAlpha(image,pixels)),q);
6937                             }
6938                         }
6939                       q+=GetPixelChannels(image);
6940                     }
6941                     n+=GetPixelChannels(image);
6942                   }
6943 
6944                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
6945                     break;
6946                 }
6947 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6948               if (magn_methx != 1 || magn_methy != 1)
6949                 {
6950                 /*
6951                    Rescale pixels to Quantum
6952                 */
6953                    for (y=0; y < (ssize_t) image->rows; y++)
6954                    {
6955                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6956 
6957                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
6958                      {
6959                         SetPixelRed(image,ScaleShortToQuantum(
6960                           GetPixelRed(image,q)),q);
6961                         SetPixelGreen(image,ScaleShortToQuantum(
6962                           GetPixelGreen(image,q)),q);
6963                         SetPixelBlue(image,ScaleShortToQuantum(
6964                           GetPixelBlue(image,q)),q);
6965                         SetPixelAlpha(image,ScaleShortToQuantum(
6966                           GetPixelAlpha(image,q)),q);
6967                         q+=GetPixelChannels(image);
6968                      }
6969 
6970                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
6971                        break;
6972                    }
6973                 }
6974 #endif
6975                 if (logging != MagickFalse)
6976                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6977                     "  Finished MAGN processing");
6978               }
6979           }
6980 
6981         /*
6982           Crop_box is with respect to the upper left corner of the MNG.
6983         */
6984         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6985         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6986         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6987         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6988         crop_box=mng_minimum_box(crop_box,mng_info->clip);
6989         crop_box=mng_minimum_box(crop_box,mng_info->frame);
6990         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6991         if ((crop_box.left != (mng_info->image_box.left
6992             +mng_info->x_off[object_id])) ||
6993             (crop_box.right != (mng_info->image_box.right
6994             +mng_info->x_off[object_id])) ||
6995             (crop_box.top != (mng_info->image_box.top
6996             +mng_info->y_off[object_id])) ||
6997             (crop_box.bottom != (mng_info->image_box.bottom
6998             +mng_info->y_off[object_id])))
6999           {
7000             if (logging != MagickFalse)
7001               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7002                 "  Crop the PNG image");
7003 
7004             if ((crop_box.left < crop_box.right) &&
7005                 (crop_box.top < crop_box.bottom))
7006               {
7007                 Image
7008                   *im;
7009 
7010                 RectangleInfo
7011                   crop_info;
7012 
7013                 /*
7014                   Crop_info is with respect to the upper left corner of
7015                   the image.
7016                 */
7017                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7018                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7019                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7020                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7021                 image->page.width=image->columns;
7022                 image->page.height=image->rows;
7023                 image->page.x=0;
7024                 image->page.y=0;
7025                 im=CropImage(image,&crop_info,exception);
7026 
7027                 if (im != (Image *) NULL)
7028                   {
7029                     image->columns=im->columns;
7030                     image->rows=im->rows;
7031                     im=DestroyImage(im);
7032                     image->page.width=image->columns;
7033                     image->page.height=image->rows;
7034                     image->page.x=crop_box.left;
7035                     image->page.y=crop_box.top;
7036                   }
7037               }
7038 
7039             else
7040               {
7041                 /*
7042                   No pixels in crop area.  The MNG spec still requires
7043                   a layer, though, so make a single transparent pixel in
7044                   the top left corner.
7045                 */
7046                 image->columns=1;
7047                 image->rows=1;
7048                 image->colors=2;
7049                 (void) SetImageBackgroundColor(image,exception);
7050                 image->page.width=1;
7051                 image->page.height=1;
7052                 image->page.x=0;
7053                 image->page.y=0;
7054               }
7055           }
7056 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7057         image=mng_info->image;
7058 #endif
7059       }
7060 
7061 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7062       /* PNG does not handle depths greater than 16 so reduce it even
7063        * if lossy.
7064        */
7065       if (image->depth > 16)
7066          image->depth=16;
7067 #endif
7068 
7069 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7070       if (image->depth > 8)
7071         {
7072           /* To do: fill low byte properly */
7073           image->depth=16;
7074         }
7075 
7076       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7077          image->depth = 8;
7078 #endif
7079 
7080       if (image_info->number_scenes != 0)
7081         {
7082           if (mng_info->scenes_found >
7083              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7084             break;
7085         }
7086 
7087       if (logging != MagickFalse)
7088         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7089           "  Finished reading image datastream.");
7090 
7091   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7092 
7093   (void) CloseBlob(image);
7094 
7095   if (logging != MagickFalse)
7096     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7097       "  Finished reading all image datastreams.");
7098 
7099 #if defined(MNG_INSERT_LAYERS)
7100   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7101        (mng_info->mng_height))
7102     {
7103       /*
7104         Insert a background layer if nothing else was found.
7105       */
7106       if (logging != MagickFalse)
7107         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7108           "  No images found.  Inserting a background layer.");
7109 
7110       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7111         {
7112           /*
7113             Allocate next image structure.
7114           */
7115           AcquireNextImage(image_info,image,exception);
7116           if (GetNextImageInList(image) == (Image *) NULL)
7117             {
7118               image=DestroyImageList(image);
7119               MngInfoFreeStruct(mng_info,&have_mng_structure);
7120 
7121               if (logging != MagickFalse)
7122                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7123                   "  Allocation failed, returning NULL.");
7124 
7125               return((Image *) NULL);
7126             }
7127           image=SyncNextImageInList(image);
7128         }
7129       image->columns=mng_info->mng_width;
7130       image->rows=mng_info->mng_height;
7131       image->page.width=mng_info->mng_width;
7132       image->page.height=mng_info->mng_height;
7133       image->page.x=0;
7134       image->page.y=0;
7135       image->background_color=mng_background_color;
7136       image->alpha_trait=UndefinedPixelTrait;
7137 
7138       if (image_info->ping == MagickFalse)
7139         (void) SetImageBackgroundColor(image,exception);
7140 
7141       mng_info->image_found++;
7142     }
7143 #endif
7144   image->iterations=mng_iterations;
7145 
7146   if (mng_iterations == 1)
7147     image->start_loop=MagickTrue;
7148 
7149   while (GetPreviousImageInList(image) != (Image *) NULL)
7150   {
7151     image_count++;
7152     if (image_count > 10*mng_info->image_found)
7153       {
7154         if (logging != MagickFalse)
7155           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7156 
7157         (void) ThrowMagickException(exception,GetMagickModule(),
7158           CoderError,"Linked list is corrupted, beginning of list not found",
7159           "`%s'",image_info->filename);
7160 
7161         return((Image *) NULL);
7162       }
7163 
7164     image=GetPreviousImageInList(image);
7165 
7166     if (GetNextImageInList(image) == (Image *) NULL)
7167       {
7168         if (logging != MagickFalse)
7169           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7170 
7171         (void) ThrowMagickException(exception,GetMagickModule(),
7172           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7173           image_info->filename);
7174       }
7175   }
7176 
7177   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7178              GetNextImageInList(image) ==
7179      (Image *) NULL)
7180     {
7181       if (logging != MagickFalse)
7182         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7183             "  First image null");
7184 
7185       (void) ThrowMagickException(exception,GetMagickModule(),
7186         CoderError,"image->next for first image is NULL but shouldn't be.",
7187         "`%s'",image_info->filename);
7188     }
7189 
7190   if (mng_info->image_found == 0)
7191     {
7192       if (logging != MagickFalse)
7193         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7194           "  No visible images found.");
7195 
7196       (void) ThrowMagickException(exception,GetMagickModule(),
7197         CoderError,"No visible images in file","`%s'",image_info->filename);
7198 
7199       if (image != (Image *) NULL)
7200         image=DestroyImageList(image);
7201 
7202       MngInfoFreeStruct(mng_info,&have_mng_structure);
7203       return((Image *) NULL);
7204     }
7205 
7206   if (mng_info->ticks_per_second)
7207     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7208             final_delay/mng_info->ticks_per_second;
7209 
7210   else
7211     image->start_loop=MagickTrue;
7212 
7213   /* Find final nonzero image delay */
7214   final_image_delay=0;
7215 
7216   while (GetNextImageInList(image) != (Image *) NULL)
7217     {
7218       if (image->delay)
7219         final_image_delay=image->delay;
7220 
7221       image=GetNextImageInList(image);
7222     }
7223 
7224   if (final_delay < final_image_delay)
7225     final_delay=final_image_delay;
7226 
7227   image->delay=final_delay;
7228 
7229   if (logging != MagickFalse)
7230       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7231         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7232         (double) final_delay);
7233 
7234   if (logging != MagickFalse)
7235     {
7236       int
7237         scene;
7238 
7239       scene=0;
7240       image=GetFirstImageInList(image);
7241 
7242       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7243         "  Before coalesce:");
7244 
7245       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7246         "    scene 0 delay=%.20g",(double) image->delay);
7247 
7248       while (GetNextImageInList(image) != (Image *) NULL)
7249       {
7250         image=GetNextImageInList(image);
7251         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7252           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7253       }
7254     }
7255 
7256   image=GetFirstImageInList(image);
7257 #ifdef MNG_COALESCE_LAYERS
7258   if (insert_layers)
7259     {
7260       Image
7261         *next_image,
7262         *next;
7263 
7264       size_t
7265         scene;
7266 
7267       if (logging != MagickFalse)
7268         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
7269 
7270       scene=image->scene;
7271       next_image=CoalesceImages(image,exception);
7272 
7273       if (next_image == (Image *) NULL)
7274         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7275 
7276       image=DestroyImageList(image);
7277       image=next_image;
7278 
7279       for (next=image; next != (Image *) NULL; next=next_image)
7280       {
7281          next->page.width=mng_info->mng_width;
7282          next->page.height=mng_info->mng_height;
7283          next->page.x=0;
7284          next->page.y=0;
7285          next->scene=scene++;
7286          next_image=GetNextImageInList(next);
7287 
7288          if (next_image == (Image *) NULL)
7289            break;
7290 
7291          if (next->delay == 0)
7292            {
7293              scene--;
7294              next_image->previous=GetPreviousImageInList(next);
7295              if (GetPreviousImageInList(next) == (Image *) NULL)
7296                image=next_image;
7297              else
7298                next->previous->next=next_image;
7299              next=DestroyImage(next);
7300            }
7301       }
7302     }
7303 #endif
7304 
7305   while (GetNextImageInList(image) != (Image *) NULL)
7306       image=GetNextImageInList(image);
7307 
7308   image->dispose=BackgroundDispose;
7309 
7310   if (logging != MagickFalse)
7311     {
7312       int
7313         scene;
7314 
7315       scene=0;
7316       image=GetFirstImageInList(image);
7317 
7318       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7319         "  After coalesce:");
7320 
7321       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7322         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7323         (double) image->dispose);
7324 
7325       while (GetNextImageInList(image) != (Image *) NULL)
7326       {
7327         image=GetNextImageInList(image);
7328 
7329         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7330           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7331           (double) image->delay,(double) image->dispose);
7332       }
7333    }
7334 
7335   image=GetFirstImageInList(image);
7336   MngInfoFreeStruct(mng_info,&have_mng_structure);
7337   have_mng_structure=MagickFalse;
7338 
7339   if (logging != MagickFalse)
7340     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7341 
7342   return(GetFirstImageInList(image));
7343 }
7344 #else /* PNG_LIBPNG_VER > 10011 */
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7345 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7346 {
7347   printf("Your PNG library is too old: You have libpng-%s\n",
7348      PNG_LIBPNG_VER_STRING);
7349 
7350   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7351     "PNG library is too old","`%s'",image_info->filename);
7352 
7353   return(Image *) NULL;
7354 }
7355 
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7356 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7357 {
7358   return(ReadPNGImage(image_info,exception));
7359 }
7360 #endif /* PNG_LIBPNG_VER > 10011 */
7361 #endif
7362 
7363 /*
7364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7365 %                                                                             %
7366 %                                                                             %
7367 %                                                                             %
7368 %   R e g i s t e r P N G I m a g e                                           %
7369 %                                                                             %
7370 %                                                                             %
7371 %                                                                             %
7372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7373 %
7374 %  RegisterPNGImage() adds properties for the PNG image format to
7375 %  the list of supported formats.  The properties include the image format
7376 %  tag, a method to read and/or write the format, whether the format
7377 %  supports the saving of more than one frame to the same file or blob,
7378 %  whether the format supports native in-memory I/O, and a brief
7379 %  description of the format.
7380 %
7381 %  The format of the RegisterPNGImage method is:
7382 %
7383 %      size_t RegisterPNGImage(void)
7384 %
7385 */
RegisterPNGImage(void)7386 ModuleExport size_t RegisterPNGImage(void)
7387 {
7388   char
7389     version[MagickPathExtent];
7390 
7391   MagickInfo
7392     *entry;
7393 
7394   static const char
7395     *PNGNote=
7396     {
7397       "See http://www.libpng.org/ for details about the PNG format."
7398     },
7399 
7400     *JNGNote=
7401     {
7402       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7403       "format."
7404     },
7405 
7406     *MNGNote=
7407     {
7408       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7409       "format."
7410     };
7411 
7412   *version='\0';
7413 
7414 #if defined(PNG_LIBPNG_VER_STRING)
7415   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7416   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MagickPathExtent);
7417 
7418   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7419     {
7420       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7421       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7422             MagickPathExtent);
7423     }
7424 #endif
7425 
7426   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7427   entry->flags|=CoderSeekableStreamFlag;  /* To do: eliminate this. */
7428 
7429 #if defined(MAGICKCORE_PNG_DELEGATE)
7430   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7431   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7432 #endif
7433 
7434   entry->magick=(IsImageFormatHandler *) IsMNG;
7435 
7436   if (*version != '\0')
7437     entry->version=ConstantString(version);
7438 
7439   entry->mime_type=ConstantString("video/x-mng");
7440   entry->note=ConstantString(MNGNote);
7441   (void) RegisterMagickInfo(entry);
7442 
7443   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7444 
7445 #if defined(MAGICKCORE_PNG_DELEGATE)
7446   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7447   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7448 #endif
7449 
7450   entry->magick=(IsImageFormatHandler *) IsPNG;
7451   entry->flags^=CoderAdjoinFlag;
7452   entry->mime_type=ConstantString("image/png");
7453 
7454   if (*version != '\0')
7455     entry->version=ConstantString(version);
7456 
7457   entry->note=ConstantString(PNGNote);
7458   (void) RegisterMagickInfo(entry);
7459 
7460   entry=AcquireMagickInfo("PNG","PNG8",
7461     "8-bit indexed with optional binary transparency");
7462 
7463 #if defined(MAGICKCORE_PNG_DELEGATE)
7464   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7465   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7466 #endif
7467 
7468   entry->magick=(IsImageFormatHandler *) IsPNG;
7469   entry->flags^=CoderAdjoinFlag;
7470   entry->mime_type=ConstantString("image/png");
7471   (void) RegisterMagickInfo(entry);
7472 
7473   entry=AcquireMagickInfo("PNG","PNG24",
7474     "opaque or binary transparent 24-bit RGB");
7475   *version='\0';
7476 
7477 #if defined(ZLIB_VERSION)
7478   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7479   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7480 
7481   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7482     {
7483       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7484       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7485     }
7486 #endif
7487 
7488   if (*version != '\0')
7489     entry->version=ConstantString(version);
7490 
7491 #if defined(MAGICKCORE_PNG_DELEGATE)
7492   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7493   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7494 #endif
7495 
7496   entry->magick=(IsImageFormatHandler *) IsPNG;
7497   entry->flags^=CoderAdjoinFlag;
7498   entry->mime_type=ConstantString("image/png");
7499   (void) RegisterMagickInfo(entry);
7500 
7501   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7502 
7503 #if defined(MAGICKCORE_PNG_DELEGATE)
7504   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7505   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7506 #endif
7507 
7508   entry->magick=(IsImageFormatHandler *) IsPNG;
7509   entry->flags^=CoderAdjoinFlag;
7510   entry->mime_type=ConstantString("image/png");
7511   (void) RegisterMagickInfo(entry);
7512 
7513   entry=AcquireMagickInfo("PNG","PNG48",
7514     "opaque or binary transparent 48-bit RGB");
7515 
7516 #if defined(MAGICKCORE_PNG_DELEGATE)
7517   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7518   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7519 #endif
7520 
7521   entry->magick=(IsImageFormatHandler *) IsPNG;
7522   entry->flags^=CoderAdjoinFlag;
7523   entry->mime_type=ConstantString("image/png");
7524   (void) RegisterMagickInfo(entry);
7525 
7526   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7527 
7528 #if defined(MAGICKCORE_PNG_DELEGATE)
7529   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7530   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7531 #endif
7532 
7533   entry->magick=(IsImageFormatHandler *) IsPNG;
7534   entry->flags^=CoderAdjoinFlag;
7535   entry->mime_type=ConstantString("image/png");
7536   (void) RegisterMagickInfo(entry);
7537 
7538   entry=AcquireMagickInfo("PNG","PNG00",
7539     "PNG inheriting bit-depth, color-type from original, if possible");
7540 
7541 #if defined(MAGICKCORE_PNG_DELEGATE)
7542   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7543   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7544 #endif
7545 
7546   entry->magick=(IsImageFormatHandler *) IsPNG;
7547   entry->flags^=CoderAdjoinFlag;
7548   entry->mime_type=ConstantString("image/png");
7549   (void) RegisterMagickInfo(entry);
7550 
7551   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7552 
7553 #if defined(JNG_SUPPORTED)
7554 #if defined(MAGICKCORE_PNG_DELEGATE)
7555   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7556   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7557 #endif
7558 #endif
7559 
7560   entry->magick=(IsImageFormatHandler *) IsJNG;
7561   entry->flags^=CoderAdjoinFlag;
7562   entry->mime_type=ConstantString("image/x-jng");
7563   entry->note=ConstantString(JNGNote);
7564   (void) RegisterMagickInfo(entry);
7565 
7566 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7567   ping_semaphore=AcquireSemaphoreInfo();
7568 #endif
7569 
7570   return(MagickImageCoderSignature);
7571 }
7572 
7573 /*
7574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7575 %                                                                             %
7576 %                                                                             %
7577 %                                                                             %
7578 %   U n r e g i s t e r P N G I m a g e                                       %
7579 %                                                                             %
7580 %                                                                             %
7581 %                                                                             %
7582 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7583 %
7584 %  UnregisterPNGImage() removes format registrations made by the
7585 %  PNG module from the list of supported formats.
7586 %
7587 %  The format of the UnregisterPNGImage method is:
7588 %
7589 %      UnregisterPNGImage(void)
7590 %
7591 */
UnregisterPNGImage(void)7592 ModuleExport void UnregisterPNGImage(void)
7593 {
7594   (void) UnregisterMagickInfo("MNG");
7595   (void) UnregisterMagickInfo("PNG");
7596   (void) UnregisterMagickInfo("PNG8");
7597   (void) UnregisterMagickInfo("PNG24");
7598   (void) UnregisterMagickInfo("PNG32");
7599   (void) UnregisterMagickInfo("PNG48");
7600   (void) UnregisterMagickInfo("PNG64");
7601   (void) UnregisterMagickInfo("PNG00");
7602   (void) UnregisterMagickInfo("JNG");
7603 
7604 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7605   if (ping_semaphore != (SemaphoreInfo *) NULL)
7606     RelinquishSemaphoreInfo(&ping_semaphore);
7607 #endif
7608 }
7609 
7610 #if defined(MAGICKCORE_PNG_DELEGATE)
7611 #if PNG_LIBPNG_VER > 10011
7612 /*
7613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7614 %                                                                             %
7615 %                                                                             %
7616 %                                                                             %
7617 %   W r i t e M N G I m a g e                                                 %
7618 %                                                                             %
7619 %                                                                             %
7620 %                                                                             %
7621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7622 %
7623 %  WriteMNGImage() writes an image in the Portable Network Graphics
7624 %  Group's "Multiple-image Network Graphics" encoded image format.
7625 %
7626 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7627 %
7628 %  The format of the WriteMNGImage method is:
7629 %
7630 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7631 %        Image *image,ExceptionInfo *exception)
7632 %
7633 %  A description of each parameter follows.
7634 %
7635 %    o image_info: the image info.
7636 %
7637 %    o image:  The image.
7638 %
7639 %    o exception: return any errors or warnings in this structure.
7640 %
7641 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7642 %    "To do" under ReadPNGImage):
7643 %
7644 %    Preserve all unknown and not-yet-handled known chunks found in input
7645 %    PNG file and copy them  into output PNG files according to the PNG
7646 %    copying rules.
7647 %
7648 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7649 %
7650 %    Improve selection of color type (use indexed-colour or indexed-colour
7651 %    with tRNS when 256 or fewer unique RGBA values are present).
7652 %
7653 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7654 %    This will be complicated if we limit ourselves to generating MNG-LC
7655 %    files.  For now we ignore disposal method 3 and simply overlay the next
7656 %    image on it.
7657 %
7658 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7659 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7660 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7661 %
7662 %    Check for identical sRGB and replace with a global sRGB (and remove
7663 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7664 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7665 %    local gAMA/cHRM with local sRGB if appropriate).
7666 %
7667 %    Check for identical sBIT chunks and write global ones.
7668 %
7669 %    Provide option to skip writing the signature tEXt chunks.
7670 %
7671 %    Use signatures to detect identical objects and reuse the first
7672 %    instance of such objects instead of writing duplicate objects.
7673 %
7674 %    Use a smaller-than-32k value of compression window size when
7675 %    appropriate.
7676 %
7677 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7678 %    ancillary text chunks and save profiles.
7679 %
7680 %    Provide an option to force LC files (to ensure exact framing rate)
7681 %    instead of VLC.
7682 %
7683 %    Provide an option to force VLC files instead of LC, even when offsets
7684 %    are present.  This will involve expanding the embedded images with a
7685 %    transparent region at the top and/or left.
7686 */
7687 
7688 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)7689 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7690    png_info *ping_info, unsigned char *profile_type, unsigned char
7691    *profile_description, unsigned char *profile_data, png_uint_32 length)
7692 {
7693    png_textp
7694      text;
7695 
7696    register ssize_t
7697      i;
7698 
7699    unsigned char
7700      *sp;
7701 
7702    png_charp
7703      dp;
7704 
7705    png_uint_32
7706      allocated_length,
7707      description_length;
7708 
7709    unsigned char
7710      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7711 
7712    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7713       return;
7714 
7715    if (image_info->verbose)
7716      {
7717        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7718          (char *) profile_type, (double) length);
7719      }
7720 
7721 #if PNG_LIBPNG_VER >= 10400
7722    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7723 #else
7724    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7725 #endif
7726    description_length=(png_uint_32) strlen((const char *) profile_description);
7727    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7728       + description_length);
7729 #if PNG_LIBPNG_VER >= 10400
7730    text[0].text=(png_charp) png_malloc(ping,
7731       (png_alloc_size_t) allocated_length);
7732    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7733 #else
7734    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7735    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7736 #endif
7737    text[0].key[0]='\0';
7738    (void) ConcatenateMagickString(text[0].key,
7739       "Raw profile type ",MagickPathExtent);
7740    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7741    sp=profile_data;
7742    dp=text[0].text;
7743    *dp++='\n';
7744    (void) CopyMagickString(dp,(const char *) profile_description,
7745      allocated_length);
7746    dp+=description_length;
7747    *dp++='\n';
7748    (void) FormatLocaleString(dp,allocated_length-
7749      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7750    dp+=8;
7751 
7752    for (i=0; i < (ssize_t) length; i++)
7753    {
7754      if (i%36 == 0)
7755        *dp++='\n';
7756      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7757      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7758    }
7759 
7760    *dp++='\n';
7761    *dp='\0';
7762    text[0].text_length=(png_size_t) (dp-text[0].text);
7763    text[0].compression=image_info->compression == NoCompression ||
7764      (image_info->compression == UndefinedCompression &&
7765      text[0].text_length < 128) ? -1 : 0;
7766 
7767    if (text[0].text_length <= allocated_length)
7768      png_set_text(ping,ping_info,text,1);
7769 
7770    png_free(ping,text[0].text);
7771    png_free(ping,text[0].key);
7772    png_free(ping,text);
7773 }
7774 
Magick_png_write_chunk_from_profile(Image * image,const char * string,MagickBooleanType logging)7775 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7776   const char *string, MagickBooleanType logging)
7777 {
7778   char
7779     *name;
7780 
7781   const StringInfo
7782     *profile;
7783 
7784   unsigned char
7785     *data;
7786 
7787   png_uint_32 length;
7788 
7789   ResetImageProfileIterator(image);
7790 
7791   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7792   {
7793     profile=GetImageProfile(image,name);
7794 
7795     if (profile != (const StringInfo *) NULL)
7796       {
7797         StringInfo
7798           *ping_profile;
7799 
7800         if (LocaleNCompare(name,string,11) == 0)
7801           {
7802             if (logging != MagickFalse)
7803                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7804                    "  Found %s profile",name);
7805 
7806             ping_profile=CloneStringInfo(profile);
7807             data=GetStringInfoDatum(ping_profile),
7808             length=(png_uint_32) GetStringInfoLength(ping_profile);
7809             data[4]=data[3];
7810             data[3]=data[2];
7811             data[2]=data[1];
7812             data[1]=data[0];
7813             (void) WriteBlobMSBULong(image,length-5);  /* data length */
7814             (void) WriteBlob(image,length-1,data+1);
7815             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7816             ping_profile=DestroyStringInfo(ping_profile);
7817           }
7818       }
7819 
7820       name=GetNextImageProfile(image);
7821    }
7822 
7823    return(MagickTrue);
7824 }
7825 
Magick_png_color_equal(const Image * image,const Quantum * p,const PixelInfo * q)7826 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
7827   const Quantum *p, const PixelInfo *q)
7828 {
7829   MagickRealType
7830     value;
7831 
7832   value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
7833   if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
7834     return(MagickFalse);
7835   value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
7836   if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
7837     return(MagickFalse);
7838   value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
7839   if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
7840     return(MagickFalse);
7841 
7842   return(MagickTrue);
7843 }
7844 
7845 #if defined(PNG_tIME_SUPPORTED)
write_tIME_chunk(Image * image,png_struct * ping,png_info * info,const char * date,ExceptionInfo * exception)7846 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7847   const char *date,ExceptionInfo *exception)
7848 {
7849   unsigned int
7850     day,
7851     hour,
7852     minute,
7853     month,
7854     second,
7855     year;
7856 
7857   png_time
7858     ptime;
7859 
7860   time_t
7861     ttime;
7862 
7863   if (date != (const char *) NULL)
7864     {
7865       if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7866           &second) != 6)
7867         {
7868           (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7869             "Invalid date format specified for png:tIME","`%s'",
7870             image->filename);
7871           return;
7872         }
7873       ptime.year=(png_uint_16) year;
7874       ptime.month=(png_byte) month;
7875       ptime.day=(png_byte) day;
7876       ptime.hour=(png_byte) hour;
7877       ptime.minute=(png_byte) minute;
7878       ptime.second=(png_byte) second;
7879     }
7880   else
7881   {
7882     time(&ttime);
7883     png_convert_from_time_t(&ptime,ttime);
7884   }
7885   png_set_tIME(ping,info,&ptime);
7886 }
7887 #endif
7888 
7889 /* Write one PNG image */
WriteOnePNGImage(MngInfo * mng_info,const ImageInfo * IMimage_info,Image * IMimage,ExceptionInfo * exception)7890 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7891   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7892 {
7893   char
7894     im_vers[32],
7895     libpng_runv[32],
7896     libpng_vers[32],
7897     zlib_runv[32],
7898     zlib_vers[32];
7899 
7900   Image
7901     *image;
7902 
7903   ImageInfo
7904     *image_info;
7905 
7906   char
7907     s[2];
7908 
7909   const char
7910     *name,
7911     *property,
7912     *value;
7913 
7914   const StringInfo
7915     *profile;
7916 
7917   int
7918     num_passes,
7919     pass;
7920 
7921   png_byte
7922      ping_trans_alpha[256];
7923 
7924   png_color
7925      palette[257];
7926 
7927   png_color_16
7928     ping_background,
7929     ping_trans_color;
7930 
7931   png_info
7932     *ping_info;
7933 
7934   png_struct
7935     *ping;
7936 
7937   png_uint_32
7938     ping_height,
7939     ping_width;
7940 
7941   ssize_t
7942     y;
7943 
7944   MagickBooleanType
7945     image_matte,
7946     logging,
7947     matte,
7948 
7949     ping_have_blob,
7950     ping_have_cheap_transparency,
7951     ping_have_color,
7952     ping_have_non_bw,
7953     ping_have_PLTE,
7954     ping_have_bKGD,
7955     ping_have_iCCP,
7956     ping_have_pHYs,
7957     ping_have_sRGB,
7958     ping_have_tRNS,
7959 
7960     ping_exclude_bKGD,
7961     ping_exclude_cHRM,
7962     ping_exclude_date,
7963     /* ping_exclude_EXIF, */
7964     ping_exclude_gAMA,
7965     ping_exclude_iCCP,
7966     /* ping_exclude_iTXt, */
7967     ping_exclude_oFFs,
7968     ping_exclude_pHYs,
7969     ping_exclude_sRGB,
7970     ping_exclude_tEXt,
7971     ping_exclude_tIME,
7972     /* ping_exclude_tRNS, */
7973     ping_exclude_vpAg,
7974     ping_exclude_zCCP, /* hex-encoded iCCP */
7975     ping_exclude_zTXt,
7976 
7977     ping_preserve_colormap,
7978     ping_preserve_iCCP,
7979     ping_need_colortype_warning,
7980 
7981     status,
7982     tried_332,
7983     tried_333,
7984     tried_444;
7985 
7986   MemoryInfo
7987     *volatile pixel_info;
7988 
7989   QuantumInfo
7990     *quantum_info;
7991 
7992   PNGErrorInfo
7993     error_info;
7994 
7995   register ssize_t
7996     i,
7997     x;
7998 
7999   unsigned char
8000     *ping_pixels;
8001 
8002   volatile int
8003     image_colors,
8004     ping_bit_depth,
8005     ping_color_type,
8006     ping_interlace_method,
8007     ping_compression_method,
8008     ping_filter_method,
8009     ping_num_trans;
8010 
8011   volatile size_t
8012     image_depth,
8013     old_bit_depth;
8014 
8015   size_t
8016     quality,
8017     rowbytes,
8018     save_image_depth;
8019 
8020   int
8021     j,
8022     number_colors,
8023     number_opaque,
8024     number_semitransparent,
8025     number_transparent,
8026     ping_pHYs_unit_type;
8027 
8028   png_uint_32
8029     ping_pHYs_x_resolution,
8030     ping_pHYs_y_resolution;
8031 
8032   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8033     "  Enter WriteOnePNGImage()");
8034 
8035   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8036   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8037   if (image_info == (ImageInfo *) NULL)
8038      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8039 
8040   /* Define these outside of the following "if logging()" block so they will
8041    * show in debuggers.
8042    */
8043   *im_vers='\0';
8044   (void) ConcatenateMagickString(im_vers,
8045          MagickLibVersionText,MagickPathExtent);
8046   (void) ConcatenateMagickString(im_vers,
8047          MagickLibAddendum,MagickPathExtent);
8048 
8049   *libpng_vers='\0';
8050   (void) ConcatenateMagickString(libpng_vers,
8051          PNG_LIBPNG_VER_STRING,32);
8052   *libpng_runv='\0';
8053   (void) ConcatenateMagickString(libpng_runv,
8054          png_get_libpng_ver(NULL),32);
8055 
8056   *zlib_vers='\0';
8057   (void) ConcatenateMagickString(zlib_vers,
8058          ZLIB_VERSION,32);
8059   *zlib_runv='\0';
8060   (void) ConcatenateMagickString(zlib_runv,
8061          zlib_version,32);
8062 
8063   if (logging != MagickFalse)
8064     {
8065        LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
8066            im_vers);
8067        LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8068            libpng_vers);
8069        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8070        {
8071        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8072            libpng_runv);
8073        }
8074        LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8075            zlib_vers);
8076        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8077        {
8078        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8079            zlib_runv);
8080        }
8081     }
8082 
8083   /* Initialize some stuff */
8084   ping_bit_depth=0,
8085   ping_color_type=0,
8086   ping_interlace_method=0,
8087   ping_compression_method=0,
8088   ping_filter_method=0,
8089   ping_num_trans = 0;
8090 
8091   ping_background.red = 0;
8092   ping_background.green = 0;
8093   ping_background.blue = 0;
8094   ping_background.gray = 0;
8095   ping_background.index = 0;
8096 
8097   ping_trans_color.red=0;
8098   ping_trans_color.green=0;
8099   ping_trans_color.blue=0;
8100   ping_trans_color.gray=0;
8101 
8102   ping_pHYs_unit_type = 0;
8103   ping_pHYs_x_resolution = 0;
8104   ping_pHYs_y_resolution = 0;
8105 
8106   ping_have_blob=MagickFalse;
8107   ping_have_cheap_transparency=MagickFalse;
8108   ping_have_color=MagickTrue;
8109   ping_have_non_bw=MagickTrue;
8110   ping_have_PLTE=MagickFalse;
8111   ping_have_bKGD=MagickFalse;
8112   ping_have_iCCP=MagickFalse;
8113   ping_have_pHYs=MagickFalse;
8114   ping_have_sRGB=MagickFalse;
8115   ping_have_tRNS=MagickFalse;
8116 
8117   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8118   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8119   ping_exclude_date=mng_info->ping_exclude_date;
8120   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8121   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8122   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8123   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8124   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8125   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8126   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8127   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8128   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8129   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8130   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8131   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8132   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8133 
8134   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8135   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8136   ping_need_colortype_warning = MagickFalse;
8137 
8138   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8139    * i.e., eliminate the ICC profile and set image->rendering_intent.
8140    * Note that this will not involve any changes to the actual pixels
8141    * but merely passes information to applications that read the resulting
8142    * PNG image.
8143    *
8144    * To do: recognize other variants of the sRGB profile, using the CRC to
8145    * verify all recognized variants including the 7 already known.
8146    *
8147    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8148    *
8149    * Use something other than image->rendering_intent to record the fact
8150    * that the sRGB profile was found.
8151    *
8152    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8153    * profile.  Record the Blackpoint Compensation, if any.
8154    */
8155    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8156    {
8157       char
8158         *name;
8159 
8160       const StringInfo
8161         *profile;
8162 
8163       ResetImageProfileIterator(image);
8164       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8165       {
8166         profile=GetImageProfile(image,name);
8167 
8168         if (profile != (StringInfo *) NULL)
8169           {
8170             if ((LocaleCompare(name,"ICC") == 0) ||
8171                 (LocaleCompare(name,"ICM") == 0))
8172 
8173              {
8174                  int
8175                    icheck,
8176                    got_crc=0;
8177 
8178 
8179                  png_uint_32
8180                    length,
8181                    profile_crc=0;
8182 
8183                  unsigned char
8184                    *data;
8185 
8186                  length=(png_uint_32) GetStringInfoLength(profile);
8187 
8188                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8189                  {
8190                    if (length == sRGB_info[icheck].len)
8191                    {
8192                      if (got_crc == 0)
8193                      {
8194                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8195                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8196                          (unsigned long) length);
8197 
8198                        data=GetStringInfoDatum(profile);
8199                        profile_crc=crc32(0,data,length);
8200 
8201                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8202                            "      with crc=%8x",(unsigned int) profile_crc);
8203                        got_crc++;
8204                      }
8205 
8206                      if (profile_crc == sRGB_info[icheck].crc)
8207                      {
8208                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8209                             "      It is sRGB with rendering intent = %s",
8210                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8211                              sRGB_info[icheck].intent));
8212                         if (image->rendering_intent==UndefinedIntent)
8213                         {
8214                           image->rendering_intent=
8215                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8216                              sRGB_info[icheck].intent);
8217                         }
8218                         ping_exclude_iCCP = MagickTrue;
8219                         ping_exclude_zCCP = MagickTrue;
8220                         ping_have_sRGB = MagickTrue;
8221                         break;
8222                      }
8223                    }
8224                  }
8225                  if (sRGB_info[icheck].len == 0)
8226                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8227                         "    Got a %lu-byte ICC profile not recognized as sRGB",
8228                         (unsigned long) length);
8229               }
8230           }
8231         name=GetNextImageProfile(image);
8232       }
8233   }
8234 
8235   number_opaque = 0;
8236   number_semitransparent = 0;
8237   number_transparent = 0;
8238 
8239   if (logging != MagickFalse)
8240     {
8241       if (image->storage_class == UndefinedClass)
8242           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8243           "    image->storage_class=UndefinedClass");
8244       if (image->storage_class == DirectClass)
8245           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8246           "    image->storage_class=DirectClass");
8247       if (image->storage_class == PseudoClass)
8248           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8249           "    image->storage_class=PseudoClass");
8250       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8251           "    image->taint=MagickTrue":
8252           "    image->taint=MagickFalse");
8253     }
8254 
8255   if (image->storage_class == PseudoClass &&
8256      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8257      mng_info->write_png48 || mng_info->write_png64 ||
8258      (mng_info->write_png_colortype != 1 &&
8259      mng_info->write_png_colortype != 5)))
8260     {
8261       (void) SyncImage(image,exception);
8262       image->storage_class = DirectClass;
8263     }
8264 
8265   if (ping_preserve_colormap == MagickFalse)
8266     {
8267       if (image->storage_class != PseudoClass && image->colormap != NULL)
8268         {
8269           /* Free the bogus colormap; it can cause trouble later */
8270            if (logging != MagickFalse)
8271               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8272               "    Freeing bogus colormap");
8273            (void) RelinquishMagickMemory(image->colormap);
8274            image->colormap=NULL;
8275         }
8276     }
8277 
8278   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8279     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8280 
8281   /*
8282     Sometimes we get PseudoClass images whose RGB values don't match
8283     the colors in the colormap.  This code syncs the RGB values.
8284   */
8285   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8286      (void) SyncImage(image,exception);
8287 
8288 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8289   if (image->depth > 8)
8290     {
8291       if (logging != MagickFalse)
8292         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8293           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8294 
8295       image->depth=8;
8296     }
8297 #endif
8298 
8299   /* Respect the -depth option */
8300   if (image->depth < 4)
8301     {
8302        register Quantum
8303          *r;
8304 
8305        if (image->depth > 2)
8306          {
8307            /* Scale to 4-bit */
8308            LBR04PacketRGBO(image->background_color);
8309 
8310            for (y=0; y < (ssize_t) image->rows; y++)
8311            {
8312              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8313 
8314              if (r == (Quantum *) NULL)
8315                break;
8316 
8317              for (x=0; x < (ssize_t) image->columns; x++)
8318              {
8319                 LBR04PixelRGBA(r);
8320                 r+=GetPixelChannels(image);
8321              }
8322 
8323              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8324                 break;
8325            }
8326 
8327            if (image->storage_class == PseudoClass && image->colormap != NULL)
8328            {
8329              for (i=0; i < (ssize_t) image->colors; i++)
8330              {
8331                LBR04PacketRGBO(image->colormap[i]);
8332              }
8333            }
8334          }
8335        else if (image->depth > 1)
8336          {
8337            /* Scale to 2-bit */
8338            LBR02PacketRGBO(image->background_color);
8339 
8340            for (y=0; y < (ssize_t) image->rows; y++)
8341            {
8342              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8343 
8344              if (r == (Quantum *) NULL)
8345                break;
8346 
8347              for (x=0; x < (ssize_t) image->columns; x++)
8348              {
8349                 LBR02PixelRGBA(r);
8350                 r+=GetPixelChannels(image);
8351              }
8352 
8353              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8354                 break;
8355            }
8356 
8357            if (image->storage_class == PseudoClass && image->colormap != NULL)
8358            {
8359              for (i=0; i < (ssize_t) image->colors; i++)
8360              {
8361                LBR02PacketRGBO(image->colormap[i]);
8362              }
8363            }
8364          }
8365        else
8366          {
8367            /* Scale to 1-bit */
8368            LBR01PacketRGBO(image->background_color);
8369 
8370            for (y=0; y < (ssize_t) image->rows; y++)
8371            {
8372              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8373 
8374              if (r == (Quantum *) NULL)
8375                break;
8376 
8377              for (x=0; x < (ssize_t) image->columns; x++)
8378              {
8379                 LBR01PixelRGBA(r);
8380                 r+=GetPixelChannels(image);
8381              }
8382 
8383              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8384                 break;
8385            }
8386 
8387            if (image->storage_class == PseudoClass && image->colormap != NULL)
8388            {
8389              for (i=0; i < (ssize_t) image->colors; i++)
8390              {
8391                LBR01PacketRGBO(image->colormap[i]);
8392              }
8393            }
8394          }
8395     }
8396 
8397   /* To do: set to next higher multiple of 8 */
8398   if (image->depth < 8)
8399      image->depth=8;
8400 
8401 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8402   /* PNG does not handle depths greater than 16 so reduce it even
8403    * if lossy
8404    */
8405   if (image->depth > 8)
8406       image->depth=16;
8407 #endif
8408 
8409 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8410   if (image->depth > 8)
8411     {
8412       /* To do: fill low byte properly */
8413       image->depth=16;
8414     }
8415 
8416   if (image->depth == 16 && mng_info->write_png_depth != 16)
8417     if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8418       image->depth = 8;
8419 #endif
8420 
8421   image_colors = (int) image->colors;
8422   number_opaque = (int) image->colors;
8423   number_transparent = 0;
8424   number_semitransparent = 0;
8425 
8426   if (mng_info->write_png_colortype &&
8427      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8428      mng_info->write_png_colortype < 4 &&
8429      image->alpha_trait == UndefinedPixelTrait)))
8430   {
8431      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8432       * are not going to need the result.
8433       */
8434      if (mng_info->write_png_colortype == 1 ||
8435         mng_info->write_png_colortype == 5)
8436        ping_have_color=MagickFalse;
8437 
8438      if (image->alpha_trait != UndefinedPixelTrait)
8439        {
8440          number_transparent = 2;
8441          number_semitransparent = 1;
8442        }
8443   }
8444 
8445   if (mng_info->write_png_colortype < 7)
8446   {
8447   /* BUILD_PALETTE
8448    *
8449    * Normally we run this just once, but in the case of writing PNG8
8450    * we reduce the transparency to binary and run again, then if there
8451    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8452    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8453    * palette.  Then (To do) we take care of a final reduction that is only
8454    * needed if there are still 256 colors present and one of them has both
8455    * transparent and opaque instances.
8456    */
8457 
8458   tried_332 = MagickFalse;
8459   tried_333 = MagickFalse;
8460   tried_444 = MagickFalse;
8461 
8462   for (j=0; j<6; j++)
8463   {
8464     /*
8465      * Sometimes we get DirectClass images that have 256 colors or fewer.
8466      * This code will build a colormap.
8467      *
8468      * Also, sometimes we get PseudoClass images with an out-of-date
8469      * colormap.  This code will replace the colormap with a new one.
8470      * Sometimes we get PseudoClass images that have more than 256 colors.
8471      * This code will delete the colormap and change the image to
8472      * DirectClass.
8473      *
8474      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8475      * even though it sometimes contains left-over non-opaque values.
8476      *
8477      * Also we gather some information (number of opaque, transparent,
8478      * and semitransparent pixels, and whether the image has any non-gray
8479      * pixels or only black-and-white pixels) that we might need later.
8480      *
8481      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8482      * we need to check for bogus non-opaque values, at least.
8483      */
8484 
8485    int
8486      n;
8487 
8488    PixelInfo
8489      opaque[260],
8490      semitransparent[260],
8491      transparent[260];
8492 
8493    register const Quantum
8494      *s;
8495 
8496    register Quantum
8497      *q,
8498      *r;
8499 
8500    if (logging != MagickFalse)
8501      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8502          "    Enter BUILD_PALETTE:");
8503 
8504    if (logging != MagickFalse)
8505      {
8506        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8507              "      image->columns=%.20g",(double) image->columns);
8508        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8509              "      image->rows=%.20g",(double) image->rows);
8510        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8511              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8512        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8513              "      image->depth=%.20g",(double) image->depth);
8514 
8515        if (image->storage_class == PseudoClass && image->colormap != NULL)
8516        {
8517          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8518              "      Original colormap:");
8519          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8520              "        i    (red,green,blue,alpha)");
8521 
8522          for (i=0; i < 256; i++)
8523          {
8524                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8525                    "        %d    (%d,%d,%d,%d)",
8526                     (int) i,
8527                     (int) image->colormap[i].red,
8528                     (int) image->colormap[i].green,
8529                     (int) image->colormap[i].blue,
8530                     (int) image->colormap[i].alpha);
8531          }
8532 
8533          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8534          {
8535            if (i > 255)
8536              {
8537                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8538                    "        %d    (%d,%d,%d,%d)",
8539                     (int) i,
8540                     (int) image->colormap[i].red,
8541                     (int) image->colormap[i].green,
8542                     (int) image->colormap[i].blue,
8543                     (int) image->colormap[i].alpha);
8544              }
8545          }
8546        }
8547 
8548        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8549            "      image->colors=%d",(int) image->colors);
8550 
8551        if (image->colors == 0)
8552          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8553              "        (zero means unknown)");
8554 
8555        if (ping_preserve_colormap == MagickFalse)
8556          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8557               "      Regenerate the colormap");
8558      }
8559 
8560      image_colors=0;
8561      number_opaque = 0;
8562      number_semitransparent = 0;
8563      number_transparent = 0;
8564 
8565      for (y=0; y < (ssize_t) image->rows; y++)
8566      {
8567        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8568 
8569        if (q == (Quantum *) NULL)
8570          break;
8571 
8572        for (x=0; x < (ssize_t) image->columns; x++)
8573        {
8574            if (image->alpha_trait == UndefinedPixelTrait ||
8575               GetPixelAlpha(image,q) == OpaqueAlpha)
8576              {
8577                if (number_opaque < 259)
8578                  {
8579                    if (number_opaque == 0)
8580                      {
8581                        GetPixelInfoPixel(image, q, opaque);
8582                        opaque[0].alpha=OpaqueAlpha;
8583                        number_opaque=1;
8584                      }
8585 
8586                    for (i=0; i< (ssize_t) number_opaque; i++)
8587                      {
8588                        if (Magick_png_color_equal(image,q,opaque+i))
8589                          break;
8590                      }
8591 
8592                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8593                      {
8594                        number_opaque++;
8595                        GetPixelInfoPixel(image, q, opaque+i);
8596                        opaque[i].alpha=OpaqueAlpha;
8597                      }
8598                  }
8599              }
8600            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8601              {
8602                if (number_transparent < 259)
8603                  {
8604                    if (number_transparent == 0)
8605                      {
8606                        GetPixelInfoPixel(image, q, transparent);
8607                        ping_trans_color.red=(unsigned short)
8608                          GetPixelRed(image,q);
8609                        ping_trans_color.green=(unsigned short)
8610                          GetPixelGreen(image,q);
8611                        ping_trans_color.blue=(unsigned short)
8612                          GetPixelBlue(image,q);
8613                        ping_trans_color.gray=(unsigned short)
8614                          GetPixelGray(image,q);
8615                        number_transparent = 1;
8616                      }
8617 
8618                    for (i=0; i< (ssize_t) number_transparent; i++)
8619                      {
8620                        if (Magick_png_color_equal(image,q,transparent+i))
8621                          break;
8622                      }
8623 
8624                    if (i ==  (ssize_t) number_transparent &&
8625                        number_transparent < 259)
8626                      {
8627                        number_transparent++;
8628                        GetPixelInfoPixel(image,q,transparent+i);
8629                      }
8630                  }
8631              }
8632            else
8633              {
8634                if (number_semitransparent < 259)
8635                  {
8636                    if (number_semitransparent == 0)
8637                      {
8638                        GetPixelInfoPixel(image,q,semitransparent);
8639                        number_semitransparent = 1;
8640                      }
8641 
8642                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8643                      {
8644                        if (Magick_png_color_equal(image,q,semitransparent+i)
8645                            && GetPixelAlpha(image,q) ==
8646                            semitransparent[i].alpha)
8647                          break;
8648                      }
8649 
8650                    if (i ==  (ssize_t) number_semitransparent &&
8651                        number_semitransparent < 259)
8652                      {
8653                        number_semitransparent++;
8654                        GetPixelInfoPixel(image, q, semitransparent+i);
8655                      }
8656                  }
8657              }
8658            q+=GetPixelChannels(image);
8659         }
8660      }
8661 
8662      if (mng_info->write_png8 == MagickFalse &&
8663          ping_exclude_bKGD == MagickFalse)
8664        {
8665          /* Add the background color to the palette, if it
8666           * isn't already there.
8667           */
8668           if (logging != MagickFalse)
8669             {
8670               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8671                   "      Check colormap for background (%d,%d,%d)",
8672                   (int) image->background_color.red,
8673                   (int) image->background_color.green,
8674                   (int) image->background_color.blue);
8675             }
8676           for (i=0; i<number_opaque; i++)
8677           {
8678              if (opaque[i].red == image->background_color.red &&
8679                  opaque[i].green == image->background_color.green &&
8680                  opaque[i].blue == image->background_color.blue)
8681                break;
8682           }
8683           if (number_opaque < 259 && i == number_opaque)
8684             {
8685                opaque[i] = image->background_color;
8686                ping_background.index = i;
8687                number_opaque++;
8688                if (logging != MagickFalse)
8689                  {
8690                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8691                        "      background_color index is %d",(int) i);
8692                  }
8693 
8694             }
8695           else if (logging != MagickFalse)
8696               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8697                   "      No room in the colormap to add background color");
8698        }
8699 
8700      image_colors=number_opaque+number_transparent+number_semitransparent;
8701 
8702      if (logging != MagickFalse)
8703        {
8704          if (image_colors > 256)
8705             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8706                   "      image has more than 256 colors");
8707 
8708          else
8709             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8710                   "      image has %d colors",image_colors);
8711        }
8712 
8713      if (ping_preserve_colormap != MagickFalse)
8714        break;
8715 
8716      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8717        {
8718          ping_have_color=MagickFalse;
8719          ping_have_non_bw=MagickFalse;
8720 
8721          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8722          {
8723            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8724               "incompatible colorspace");
8725            ping_have_color=MagickTrue;
8726            ping_have_non_bw=MagickTrue;
8727          }
8728 
8729          if(image_colors > 256)
8730            {
8731              for (y=0; y < (ssize_t) image->rows; y++)
8732              {
8733                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8734 
8735                if (q == (Quantum *) NULL)
8736                  break;
8737 
8738                s=q;
8739                for (x=0; x < (ssize_t) image->columns; x++)
8740                {
8741                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8742                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8743                    {
8744                       ping_have_color=MagickTrue;
8745                       ping_have_non_bw=MagickTrue;
8746                       break;
8747                    }
8748                  s+=GetPixelChannels(image);
8749                }
8750 
8751                if (ping_have_color != MagickFalse)
8752                  break;
8753 
8754                /* Worst case is black-and-white; we are looking at every
8755                 * pixel twice.
8756                 */
8757 
8758                if (ping_have_non_bw == MagickFalse)
8759                  {
8760                    s=q;
8761                    for (x=0; x < (ssize_t) image->columns; x++)
8762                    {
8763                      if (GetPixelRed(image,s) != 0 &&
8764                          GetPixelRed(image,s) != QuantumRange)
8765                        {
8766                          ping_have_non_bw=MagickTrue;
8767                          break;
8768                        }
8769                      s+=GetPixelChannels(image);
8770                    }
8771                }
8772              }
8773            }
8774        }
8775 
8776      if (image_colors < 257)
8777        {
8778          PixelInfo
8779            colormap[260];
8780 
8781          /*
8782           * Initialize image colormap.
8783           */
8784 
8785          if (logging != MagickFalse)
8786             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8787                   "      Sort the new colormap");
8788 
8789         /* Sort palette, transparent first */;
8790 
8791          n = 0;
8792 
8793          for (i=0; i<number_transparent; i++)
8794             colormap[n++] = transparent[i];
8795 
8796          for (i=0; i<number_semitransparent; i++)
8797             colormap[n++] = semitransparent[i];
8798 
8799          for (i=0; i<number_opaque; i++)
8800             colormap[n++] = opaque[i];
8801 
8802          ping_background.index +=
8803            (number_transparent + number_semitransparent);
8804 
8805          /* image_colors < 257; search the colormap instead of the pixels
8806           * to get ping_have_color and ping_have_non_bw
8807           */
8808          for (i=0; i<n; i++)
8809          {
8810            if (ping_have_color == MagickFalse)
8811              {
8812                 if (colormap[i].red != colormap[i].green ||
8813                     colormap[i].red != colormap[i].blue)
8814                   {
8815                      ping_have_color=MagickTrue;
8816                      ping_have_non_bw=MagickTrue;
8817                      break;
8818                   }
8819               }
8820 
8821            if (ping_have_non_bw == MagickFalse)
8822              {
8823                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8824                    ping_have_non_bw=MagickTrue;
8825              }
8826           }
8827 
8828         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8829             (number_transparent == 0 && number_semitransparent == 0)) &&
8830             (((mng_info->write_png_colortype-1) ==
8831             PNG_COLOR_TYPE_PALETTE) ||
8832             (mng_info->write_png_colortype == 0)))
8833           {
8834             if (logging != MagickFalse)
8835               {
8836                 if (n !=  (ssize_t) image_colors)
8837                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8838                    "   image_colors (%d) and n (%d)  don't match",
8839                    image_colors, n);
8840 
8841                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8842                    "      AcquireImageColormap");
8843               }
8844 
8845             image->colors = image_colors;
8846 
8847             if (AcquireImageColormap(image,image_colors,exception) ==
8848                 MagickFalse)
8849                ThrowWriterException(ResourceLimitError,
8850                    "MemoryAllocationFailed");
8851 
8852             for (i=0; i< (ssize_t) image_colors; i++)
8853                image->colormap[i] = colormap[i];
8854 
8855             if (logging != MagickFalse)
8856               {
8857                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8858                       "      image->colors=%d (%d)",
8859                       (int) image->colors, image_colors);
8860 
8861                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8862                       "      Update the pixel indexes");
8863               }
8864 
8865             /* Sync the pixel indices with the new colormap */
8866 
8867             for (y=0; y < (ssize_t) image->rows; y++)
8868             {
8869               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8870 
8871               if (q == (Quantum *) NULL)
8872                 break;
8873 
8874               for (x=0; x < (ssize_t) image->columns; x++)
8875               {
8876                 for (i=0; i< (ssize_t) image_colors; i++)
8877                 {
8878                   if ((image->alpha_trait == UndefinedPixelTrait ||
8879                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8880                       image->colormap[i].red == GetPixelRed(image,q) &&
8881                       image->colormap[i].green == GetPixelGreen(image,q) &&
8882                       image->colormap[i].blue == GetPixelBlue(image,q))
8883                   {
8884                     SetPixelIndex(image,i,q);
8885                     break;
8886                   }
8887                 }
8888                 q+=GetPixelChannels(image);
8889               }
8890 
8891               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8892                  break;
8893             }
8894           }
8895        }
8896 
8897      if (logging != MagickFalse)
8898        {
8899          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8900             "      image->colors=%d", (int) image->colors);
8901 
8902          if (image->colormap != NULL)
8903            {
8904              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8905                  "       i     (red,green,blue,alpha)");
8906 
8907              for (i=0; i < (ssize_t) image->colors; i++)
8908              {
8909                if (i < 300 || i >= (ssize_t) image->colors - 10)
8910                  {
8911                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8912                        "       %d     (%d,%d,%d,%d)",
8913                         (int) i,
8914                         (int) image->colormap[i].red,
8915                         (int) image->colormap[i].green,
8916                         (int) image->colormap[i].blue,
8917                         (int) image->colormap[i].alpha);
8918                  }
8919              }
8920            }
8921 
8922            if (number_transparent < 257)
8923              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8924                    "      number_transparent     = %d",
8925                    number_transparent);
8926            else
8927 
8928              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8929                    "      number_transparent     > 256");
8930 
8931            if (number_opaque < 257)
8932              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8933                    "      number_opaque          = %d",
8934                    number_opaque);
8935 
8936            else
8937              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8938                    "      number_opaque          > 256");
8939 
8940            if (number_semitransparent < 257)
8941              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8942                    "      number_semitransparent = %d",
8943                    number_semitransparent);
8944 
8945            else
8946              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8947                    "      number_semitransparent > 256");
8948 
8949            if (ping_have_non_bw == MagickFalse)
8950               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8951                     "      All pixels and the background are black or white");
8952 
8953            else if (ping_have_color == MagickFalse)
8954               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8955                     "      All pixels and the background are gray");
8956 
8957            else
8958               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8959                     "      At least one pixel or the background is non-gray");
8960 
8961            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8962                "    Exit BUILD_PALETTE:");
8963        }
8964 
8965    if (mng_info->write_png8 == MagickFalse)
8966       break;
8967 
8968    /* Make any reductions necessary for the PNG8 format */
8969     if (image_colors <= 256 &&
8970         image_colors != 0 && image->colormap != NULL &&
8971         number_semitransparent == 0 &&
8972         number_transparent <= 1)
8973       break;
8974 
8975     /* PNG8 can't have semitransparent colors so we threshold the
8976      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8977      * transparent color so if more than one is transparent we merge
8978      * them into image->background_color.
8979      */
8980     if (number_semitransparent != 0 || number_transparent > 1)
8981       {
8982         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8983             "    Thresholding the alpha channel to binary");
8984 
8985         for (y=0; y < (ssize_t) image->rows; y++)
8986         {
8987           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8988 
8989           if (r == (Quantum *) NULL)
8990             break;
8991 
8992           for (x=0; x < (ssize_t) image->columns; x++)
8993           {
8994               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8995                 {
8996                   SetPixelViaPixelInfo(image,&image->background_color,r);
8997                   SetPixelAlpha(image,TransparentAlpha,r);
8998                 }
8999               else
9000                   SetPixelAlpha(image,OpaqueAlpha,r);
9001               r+=GetPixelChannels(image);
9002           }
9003 
9004           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9005              break;
9006 
9007           if (image_colors != 0 && image_colors <= 256 &&
9008              image->colormap != NULL)
9009             for (i=0; i<image_colors; i++)
9010                 image->colormap[i].alpha =
9011                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9012                     TransparentAlpha : OpaqueAlpha);
9013         }
9014       continue;
9015     }
9016 
9017     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9018      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9019      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9020      * colors or less.
9021      */
9022     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9023       {
9024         if (logging != MagickFalse)
9025            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9026                "    Quantizing the background color to 4-4-4");
9027 
9028         tried_444 = MagickTrue;
9029 
9030         LBR04PacketRGB(image->background_color);
9031 
9032         if (logging != MagickFalse)
9033           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9034               "    Quantizing the pixel colors to 4-4-4");
9035 
9036         if (image->colormap == NULL)
9037         {
9038           for (y=0; y < (ssize_t) image->rows; y++)
9039           {
9040             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9041 
9042             if (r == (Quantum *) NULL)
9043               break;
9044 
9045             for (x=0; x < (ssize_t) image->columns; x++)
9046             {
9047               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9048                   LBR04PixelRGB(r);
9049               r+=GetPixelChannels(image);
9050             }
9051 
9052             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9053                break;
9054           }
9055         }
9056 
9057         else /* Should not reach this; colormap already exists and
9058                 must be <= 256 */
9059         {
9060           if (logging != MagickFalse)
9061               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9062               "    Quantizing the colormap to 4-4-4");
9063 
9064           for (i=0; i<image_colors; i++)
9065           {
9066             LBR04PacketRGB(image->colormap[i]);
9067           }
9068         }
9069         continue;
9070       }
9071 
9072     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9073       {
9074         if (logging != MagickFalse)
9075            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9076                "    Quantizing the background color to 3-3-3");
9077 
9078         tried_333 = MagickTrue;
9079 
9080         LBR03PacketRGB(image->background_color);
9081 
9082         if (logging != MagickFalse)
9083           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9084               "    Quantizing the pixel colors to 3-3-3-1");
9085 
9086         if (image->colormap == NULL)
9087         {
9088           for (y=0; y < (ssize_t) image->rows; y++)
9089           {
9090             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9091 
9092             if (r == (Quantum *) NULL)
9093               break;
9094 
9095             for (x=0; x < (ssize_t) image->columns; x++)
9096             {
9097               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9098                   LBR03RGB(r);
9099               r+=GetPixelChannels(image);
9100             }
9101 
9102             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9103                break;
9104           }
9105         }
9106 
9107         else /* Should not reach this; colormap already exists and
9108                 must be <= 256 */
9109         {
9110           if (logging != MagickFalse)
9111               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9112               "    Quantizing the colormap to 3-3-3-1");
9113           for (i=0; i<image_colors; i++)
9114           {
9115               LBR03PacketRGB(image->colormap[i]);
9116           }
9117         }
9118         continue;
9119       }
9120 
9121     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9122       {
9123         if (logging != MagickFalse)
9124            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9125                "    Quantizing the background color to 3-3-2");
9126 
9127         tried_332 = MagickTrue;
9128 
9129         /* Red and green were already done so we only quantize the blue
9130          * channel
9131          */
9132 
9133         LBR02PacketBlue(image->background_color);
9134 
9135         if (logging != MagickFalse)
9136           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9137               "    Quantizing the pixel colors to 3-3-2-1");
9138 
9139         if (image->colormap == NULL)
9140         {
9141           for (y=0; y < (ssize_t) image->rows; y++)
9142           {
9143             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9144 
9145             if (r == (Quantum *) NULL)
9146               break;
9147 
9148             for (x=0; x < (ssize_t) image->columns; x++)
9149             {
9150               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9151                   LBR02PixelBlue(r);
9152               r+=GetPixelChannels(image);
9153             }
9154 
9155             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9156                break;
9157           }
9158         }
9159 
9160         else /* Should not reach this; colormap already exists and
9161                 must be <= 256 */
9162         {
9163           if (logging != MagickFalse)
9164               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9165               "    Quantizing the colormap to 3-3-2-1");
9166           for (i=0; i<image_colors; i++)
9167           {
9168               LBR02PacketBlue(image->colormap[i]);
9169           }
9170       }
9171       continue;
9172     }
9173 
9174     if (image_colors == 0 || image_colors > 256)
9175     {
9176       /* Take care of special case with 256 opaque colors + 1 transparent
9177        * color.  We don't need to quantize to 2-3-2-1; we only need to
9178        * eliminate one color, so we'll merge the two darkest red
9179        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9180        */
9181       if (logging != MagickFalse)
9182         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9183             "    Merging two dark red background colors to 3-3-2-1");
9184 
9185       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9186           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9187           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9188       {
9189          image->background_color.red=ScaleCharToQuantum(0x24);
9190       }
9191 
9192       if (logging != MagickFalse)
9193         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9194             "    Merging two dark red pixel colors to 3-3-2-1");
9195 
9196       if (image->colormap == NULL)
9197       {
9198         for (y=0; y < (ssize_t) image->rows; y++)
9199         {
9200           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9201 
9202           if (r == (Quantum *) NULL)
9203             break;
9204 
9205           for (x=0; x < (ssize_t) image->columns; x++)
9206           {
9207             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9208                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9209                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9210                 GetPixelAlpha(image,r) == OpaqueAlpha)
9211               {
9212                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9213               }
9214             r+=GetPixelChannels(image);
9215           }
9216 
9217           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9218              break;
9219 
9220         }
9221       }
9222 
9223       else
9224       {
9225          for (i=0; i<image_colors; i++)
9226          {
9227             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9228                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9229                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9230             {
9231                image->colormap[i].red=ScaleCharToQuantum(0x24);
9232             }
9233          }
9234       }
9235     }
9236   }
9237   }
9238   /* END OF BUILD_PALETTE */
9239 
9240   /* If we are excluding the tRNS chunk and there is transparency,
9241    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9242    * PNG.
9243    */
9244   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9245      (number_transparent != 0 || number_semitransparent != 0))
9246     {
9247       unsigned int colortype=mng_info->write_png_colortype;
9248 
9249       if (ping_have_color == MagickFalse)
9250         mng_info->write_png_colortype = 5;
9251 
9252       else
9253         mng_info->write_png_colortype = 7;
9254 
9255       if (colortype != 0 &&
9256          mng_info->write_png_colortype != colortype)
9257         ping_need_colortype_warning=MagickTrue;
9258 
9259     }
9260 
9261   /* See if cheap transparency is possible.  It is only possible
9262    * when there is a single transparent color, no semitransparent
9263    * color, and no opaque color that has the same RGB components
9264    * as the transparent color.  We only need this information if
9265    * we are writing a PNG with colortype 0 or 2, and we have not
9266    * excluded the tRNS chunk.
9267    */
9268   if (number_transparent == 1 &&
9269       mng_info->write_png_colortype < 4)
9270     {
9271        ping_have_cheap_transparency = MagickTrue;
9272 
9273        if (number_semitransparent != 0)
9274          ping_have_cheap_transparency = MagickFalse;
9275 
9276        else if (image_colors == 0 || image_colors > 256 ||
9277            image->colormap == NULL)
9278          {
9279            register const Quantum
9280              *q;
9281 
9282            for (y=0; y < (ssize_t) image->rows; y++)
9283            {
9284              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9285 
9286              if (q == (Quantum *) NULL)
9287                break;
9288 
9289              for (x=0; x < (ssize_t) image->columns; x++)
9290              {
9291                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9292                      (unsigned short) GetPixelRed(image,q) ==
9293                                      ping_trans_color.red &&
9294                      (unsigned short) GetPixelGreen(image,q) ==
9295                                      ping_trans_color.green &&
9296                      (unsigned short) GetPixelBlue(image,q) ==
9297                                      ping_trans_color.blue)
9298                    {
9299                      ping_have_cheap_transparency = MagickFalse;
9300                      break;
9301                    }
9302 
9303                  q+=GetPixelChannels(image);
9304              }
9305 
9306              if (ping_have_cheap_transparency == MagickFalse)
9307                 break;
9308            }
9309          }
9310        else
9311          {
9312             /* Assuming that image->colormap[0] is the one transparent color
9313              * and that all others are opaque.
9314              */
9315             if (image_colors > 1)
9316               for (i=1; i<image_colors; i++)
9317                 if (image->colormap[i].red == image->colormap[0].red &&
9318                     image->colormap[i].green == image->colormap[0].green &&
9319                     image->colormap[i].blue == image->colormap[0].blue)
9320                   {
9321                      ping_have_cheap_transparency = MagickFalse;
9322                      break;
9323                   }
9324          }
9325 
9326        if (logging != MagickFalse)
9327          {
9328            if (ping_have_cheap_transparency == MagickFalse)
9329              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9330                  "   Cheap transparency is not possible.");
9331 
9332            else
9333              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9334                  "   Cheap transparency is possible.");
9335          }
9336      }
9337   else
9338     ping_have_cheap_transparency = MagickFalse;
9339 
9340   image_depth=image->depth;
9341 
9342   quantum_info = (QuantumInfo *) NULL;
9343   number_colors=0;
9344   image_colors=(int) image->colors;
9345   image_matte=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse;
9346 
9347   if (mng_info->write_png_colortype < 5)
9348     mng_info->IsPalette=image->storage_class == PseudoClass &&
9349       image_colors <= 256 && image->colormap != NULL;
9350   else
9351     mng_info->IsPalette = MagickFalse;
9352 
9353   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9354      (image->colors == 0 || image->colormap == NULL))
9355     {
9356       image_info=DestroyImageInfo(image_info);
9357       image=DestroyImage(image);
9358       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9359           "Cannot write PNG8 or color-type 3; colormap is NULL",
9360           "`%s'",IMimage->filename);
9361       return(MagickFalse);
9362     }
9363 
9364   /*
9365     Allocate the PNG structures
9366   */
9367 #ifdef PNG_USER_MEM_SUPPORTED
9368  error_info.image=image;
9369  error_info.exception=exception;
9370   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9371     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9372     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9373 
9374 #else
9375   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9376     MagickPNGErrorHandler,MagickPNGWarningHandler);
9377 
9378 #endif
9379   if (ping == (png_struct *) NULL)
9380     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9381 
9382   ping_info=png_create_info_struct(ping);
9383 
9384   if (ping_info == (png_info *) NULL)
9385     {
9386       png_destroy_write_struct(&ping,(png_info **) NULL);
9387       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9388     }
9389 
9390   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9391   pixel_info=(MemoryInfo *) NULL;
9392 
9393   if (setjmp(png_jmpbuf(ping)))
9394     {
9395       /*
9396         PNG write failed.
9397       */
9398 #ifdef PNG_DEBUG
9399      if (image_info->verbose)
9400         (void) printf("PNG write has failed.\n");
9401 #endif
9402       png_destroy_write_struct(&ping,&ping_info);
9403 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9404       UnlockSemaphoreInfo(ping_semaphore);
9405 #endif
9406 
9407       if (pixel_info != (MemoryInfo *) NULL)
9408         pixel_info=RelinquishVirtualMemory(pixel_info);
9409 
9410       if (quantum_info != (QuantumInfo *) NULL)
9411         quantum_info=DestroyQuantumInfo(quantum_info);
9412 
9413       if (ping_have_blob != MagickFalse)
9414           (void) CloseBlob(image);
9415       image_info=DestroyImageInfo(image_info);
9416       image=DestroyImage(image);
9417       return(MagickFalse);
9418     }
9419 
9420   /* {  For navigation to end of SETJMP-protected block.  Within this
9421    *    block, use png_error() instead of Throwing an Exception, to ensure
9422    *    that libpng is able to clean up, and that the semaphore is unlocked.
9423    */
9424 
9425 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9426   LockSemaphoreInfo(ping_semaphore);
9427 #endif
9428 
9429 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9430   /* Allow benign errors */
9431   png_set_benign_errors(ping, 1);
9432 #endif
9433 
9434 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9435   /* Reject images with too many rows or columns */
9436   png_set_user_limits(ping,
9437     (png_uint_32) MagickMin(0x7fffffffL,
9438         GetMagickResourceLimit(WidthResource)),
9439     (png_uint_32) MagickMin(0x7fffffffL,
9440         GetMagickResourceLimit(HeightResource)));
9441 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9442 
9443   /*
9444     Prepare PNG for writing.
9445   */
9446 
9447 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9448   if (mng_info->write_mng)
9449   {
9450      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9451 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9452      /* Disable new libpng-1.5.10 feature when writing a MNG because
9453       * zero-length PLTE is OK
9454       */
9455      png_set_check_for_invalid_index (ping, 0);
9456 # endif
9457   }
9458 
9459 #else
9460 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9461   if (mng_info->write_mng)
9462      png_permit_empty_plte(ping,MagickTrue);
9463 
9464 # endif
9465 #endif
9466 
9467   x=0;
9468 
9469   ping_width=(png_uint_32) image->columns;
9470   ping_height=(png_uint_32) image->rows;
9471 
9472   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9473      image_depth=8;
9474 
9475   if (mng_info->write_png48 || mng_info->write_png64)
9476      image_depth=16;
9477 
9478   if (mng_info->write_png_depth != 0)
9479      image_depth=mng_info->write_png_depth;
9480 
9481   /* Adjust requested depth to next higher valid depth if necessary */
9482   if (image_depth > 8)
9483      image_depth=16;
9484 
9485   if ((image_depth > 4) && (image_depth < 8))
9486      image_depth=8;
9487 
9488   if (image_depth == 3)
9489      image_depth=4;
9490 
9491   if (logging != MagickFalse)
9492     {
9493      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9494         "    width=%.20g",(double) ping_width);
9495      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9496         "    height=%.20g",(double) ping_height);
9497      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9498         "    image_matte=%.20g",(double) image->alpha_trait);
9499      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9500         "    image->depth=%.20g",(double) image->depth);
9501      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9502         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9503     }
9504 
9505   save_image_depth=image_depth;
9506   ping_bit_depth=(png_byte) save_image_depth;
9507 
9508 
9509 #if defined(PNG_pHYs_SUPPORTED)
9510   if (ping_exclude_pHYs == MagickFalse)
9511   {
9512   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9513       (!mng_info->write_mng || !mng_info->equal_physs))
9514     {
9515       if (logging != MagickFalse)
9516         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9517             "    Setting up pHYs chunk");
9518 
9519       if (image->units == PixelsPerInchResolution)
9520         {
9521           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9522           ping_pHYs_x_resolution=
9523              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9524           ping_pHYs_y_resolution=
9525              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9526         }
9527 
9528       else if (image->units == PixelsPerCentimeterResolution)
9529         {
9530           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9531           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9532           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9533         }
9534 
9535       else
9536         {
9537           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9538           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9539           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9540         }
9541 
9542       if (logging != MagickFalse)
9543         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9544           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9545           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9546           (int) ping_pHYs_unit_type);
9547        ping_have_pHYs = MagickTrue;
9548     }
9549   }
9550 #endif
9551 
9552   if (ping_exclude_bKGD == MagickFalse)
9553   {
9554   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9555     {
9556        unsigned int
9557          mask;
9558 
9559        mask=0xffff;
9560        if (ping_bit_depth == 8)
9561           mask=0x00ff;
9562 
9563        if (ping_bit_depth == 4)
9564           mask=0x000f;
9565 
9566        if (ping_bit_depth == 2)
9567           mask=0x0003;
9568 
9569        if (ping_bit_depth == 1)
9570           mask=0x0001;
9571 
9572        ping_background.red=(png_uint_16)
9573          (ScaleQuantumToShort(image->background_color.red) & mask);
9574 
9575        ping_background.green=(png_uint_16)
9576          (ScaleQuantumToShort(image->background_color.green) & mask);
9577 
9578        ping_background.blue=(png_uint_16)
9579          (ScaleQuantumToShort(image->background_color.blue) & mask);
9580 
9581        ping_background.gray=(png_uint_16) ping_background.green;
9582     }
9583 
9584   if (logging != MagickFalse)
9585     {
9586       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587           "    Setting up bKGD chunk (1)");
9588       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9589           "      background_color index is %d",
9590           (int) ping_background.index);
9591 
9592       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9593           "    ping_bit_depth=%d",ping_bit_depth);
9594     }
9595 
9596   ping_have_bKGD = MagickTrue;
9597   }
9598 
9599   /*
9600     Select the color type.
9601   */
9602   matte=image_matte;
9603   old_bit_depth=0;
9604 
9605   if (mng_info->IsPalette && mng_info->write_png8)
9606     {
9607       /* To do: make this a function cause it's used twice, except
9608          for reducing the sample depth from 8. */
9609 
9610       number_colors=image_colors;
9611 
9612       ping_have_tRNS=MagickFalse;
9613 
9614       /*
9615         Set image palette.
9616       */
9617       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9618 
9619       if (logging != MagickFalse)
9620         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9621             "  Setting up PLTE chunk with %d colors (%d)",
9622             number_colors, image_colors);
9623 
9624       for (i=0; i < (ssize_t) number_colors; i++)
9625       {
9626         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9627         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9628         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9629         if (logging != MagickFalse)
9630           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9631 #if MAGICKCORE_QUANTUM_DEPTH == 8
9632             "    %3ld (%3d,%3d,%3d)",
9633 #else
9634             "    %5ld (%5d,%5d,%5d)",
9635 #endif
9636             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9637 
9638       }
9639 
9640       ping_have_PLTE=MagickTrue;
9641       image_depth=ping_bit_depth;
9642       ping_num_trans=0;
9643 
9644       if (matte != MagickFalse)
9645       {
9646           /*
9647             Identify which colormap entry is transparent.
9648           */
9649           assert(number_colors <= 256);
9650           assert(image->colormap != NULL);
9651 
9652           for (i=0; i < (ssize_t) number_transparent; i++)
9653              ping_trans_alpha[i]=0;
9654 
9655 
9656           ping_num_trans=(unsigned short) (number_transparent +
9657              number_semitransparent);
9658 
9659           if (ping_num_trans == 0)
9660              ping_have_tRNS=MagickFalse;
9661 
9662           else
9663              ping_have_tRNS=MagickTrue;
9664       }
9665 
9666       if (ping_exclude_bKGD == MagickFalse)
9667       {
9668        /*
9669         * Identify which colormap entry is the background color.
9670         */
9671 
9672         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9673           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9674             break;
9675 
9676         ping_background.index=(png_byte) i;
9677 
9678         if (logging != MagickFalse)
9679           {
9680             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9681                  "      background_color index is %d",
9682                  (int) ping_background.index);
9683           }
9684       }
9685     } /* end of write_png8 */
9686 
9687   else if (mng_info->write_png_colortype == 1)
9688     {
9689       image_matte=MagickFalse;
9690       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9691     }
9692 
9693   else if (mng_info->write_png24 || mng_info->write_png48 ||
9694       mng_info->write_png_colortype == 3)
9695     {
9696       image_matte=MagickFalse;
9697       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9698     }
9699 
9700   else if (mng_info->write_png32 || mng_info->write_png64 ||
9701       mng_info->write_png_colortype == 7)
9702     {
9703       image_matte=MagickTrue;
9704       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9705     }
9706 
9707   else /* mng_info->write_pngNN not specified */
9708     {
9709       image_depth=ping_bit_depth;
9710 
9711       if (mng_info->write_png_colortype != 0)
9712         {
9713           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9714 
9715           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9716               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9717             image_matte=MagickTrue;
9718 
9719           else
9720             image_matte=MagickFalse;
9721 
9722           if (logging != MagickFalse)
9723              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9724              "   PNG colortype %d was specified:",(int) ping_color_type);
9725         }
9726 
9727       else /* write_png_colortype not specified */
9728         {
9729           if (logging != MagickFalse)
9730              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9731              "  Selecting PNG colortype:");
9732 
9733           ping_color_type=(png_byte) ((matte != MagickFalse)?
9734             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9735 
9736           if (image_info->type == TrueColorType)
9737             {
9738               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9739               image_matte=MagickFalse;
9740             }
9741 
9742           if (image_info->type == TrueColorAlphaType)
9743             {
9744               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9745               image_matte=MagickTrue;
9746             }
9747 
9748           if (image_info->type == PaletteType ||
9749               image_info->type == PaletteAlphaType)
9750             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9751 
9752           if (mng_info->write_png_colortype == 0 &&
9753              image_info->type == UndefinedType)
9754             {
9755               if (ping_have_color == MagickFalse)
9756                 {
9757                   if (image_matte == MagickFalse)
9758                     {
9759                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9760                       image_matte=MagickFalse;
9761                     }
9762 
9763                   else
9764                     {
9765                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9766                       image_matte=MagickTrue;
9767                     }
9768                 }
9769               else
9770                 {
9771                   if (image_matte == MagickFalse)
9772                     {
9773                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9774                       image_matte=MagickFalse;
9775                     }
9776 
9777                   else
9778                     {
9779                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9780                       image_matte=MagickTrue;
9781                     }
9782                  }
9783             }
9784 
9785         }
9786 
9787       if (logging != MagickFalse)
9788          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9789          "    Selected PNG colortype=%d",ping_color_type);
9790 
9791       if (ping_bit_depth < 8)
9792         {
9793           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9794               ping_color_type == PNG_COLOR_TYPE_RGB ||
9795               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9796             ping_bit_depth=8;
9797         }
9798 
9799       old_bit_depth=ping_bit_depth;
9800 
9801       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9802         {
9803           if (image->alpha_trait == UndefinedPixelTrait && ping_have_non_bw == MagickFalse)
9804              ping_bit_depth=1;
9805         }
9806 
9807       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9808         {
9809            size_t one = 1;
9810            ping_bit_depth=1;
9811 
9812            if (image->colors == 0)
9813            {
9814               /* DO SOMETHING */
9815                 png_error(ping,"image has 0 colors");
9816            }
9817 
9818            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9819              ping_bit_depth <<= 1;
9820         }
9821 
9822       if (logging != MagickFalse)
9823          {
9824            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9825             "    Number of colors: %.20g",(double) image_colors);
9826 
9827            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9828             "    Tentative PNG bit depth: %d",ping_bit_depth);
9829          }
9830 
9831       if (ping_bit_depth < (int) mng_info->write_png_depth)
9832          ping_bit_depth = mng_info->write_png_depth;
9833     }
9834 
9835   image_depth=ping_bit_depth;
9836 
9837   if (logging != MagickFalse)
9838     {
9839       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9840         "    Tentative PNG color type: %s (%.20g)",
9841         PngColorTypeToString(ping_color_type),
9842         (double) ping_color_type);
9843 
9844       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845         "    image_info->type: %.20g",(double) image_info->type);
9846 
9847       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9848         "    image_depth: %.20g",(double) image_depth);
9849 
9850       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9851 
9852         "    image->depth: %.20g",(double) image->depth);
9853 
9854       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9855         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9856     }
9857 
9858   if (matte != MagickFalse)
9859     {
9860       if (mng_info->IsPalette)
9861         {
9862           if (mng_info->write_png_colortype == 0)
9863             {
9864               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9865 
9866               if (ping_have_color != MagickFalse)
9867                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9868             }
9869 
9870           /*
9871            * Determine if there is any transparent color.
9872           */
9873           if (number_transparent + number_semitransparent == 0)
9874             {
9875               /*
9876                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9877               */
9878 
9879               image_matte=MagickFalse;
9880 
9881               if (mng_info->write_png_colortype == 0)
9882                 ping_color_type&=0x03;
9883             }
9884 
9885           else
9886             {
9887               unsigned int
9888                 mask;
9889 
9890               mask=0xffff;
9891 
9892               if (ping_bit_depth == 8)
9893                  mask=0x00ff;
9894 
9895               if (ping_bit_depth == 4)
9896                  mask=0x000f;
9897 
9898               if (ping_bit_depth == 2)
9899                  mask=0x0003;
9900 
9901               if (ping_bit_depth == 1)
9902                  mask=0x0001;
9903 
9904               ping_trans_color.red=(png_uint_16)
9905                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9906 
9907               ping_trans_color.green=(png_uint_16)
9908                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9909 
9910               ping_trans_color.blue=(png_uint_16)
9911                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9912 
9913               ping_trans_color.gray=(png_uint_16)
9914                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
9915                    image->colormap)) & mask);
9916 
9917               ping_trans_color.index=(png_byte) 0;
9918 
9919               ping_have_tRNS=MagickTrue;
9920             }
9921 
9922           if (ping_have_tRNS != MagickFalse)
9923             {
9924               /*
9925                * Determine if there is one and only one transparent color
9926                * and if so if it is fully transparent.
9927                */
9928               if (ping_have_cheap_transparency == MagickFalse)
9929                 ping_have_tRNS=MagickFalse;
9930             }
9931 
9932           if (ping_have_tRNS != MagickFalse)
9933             {
9934               if (mng_info->write_png_colortype == 0)
9935                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
9936 
9937               if (image_depth == 8)
9938                 {
9939                   ping_trans_color.red&=0xff;
9940                   ping_trans_color.green&=0xff;
9941                   ping_trans_color.blue&=0xff;
9942                   ping_trans_color.gray&=0xff;
9943                 }
9944             }
9945         }
9946       else
9947         {
9948           if (image_depth == 8)
9949             {
9950               ping_trans_color.red&=0xff;
9951               ping_trans_color.green&=0xff;
9952               ping_trans_color.blue&=0xff;
9953               ping_trans_color.gray&=0xff;
9954             }
9955         }
9956     }
9957 
9958     matte=image_matte;
9959 
9960     if (ping_have_tRNS != MagickFalse)
9961       image_matte=MagickFalse;
9962 
9963     if ((mng_info->IsPalette) &&
9964         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9965         ping_have_color == MagickFalse &&
9966         (image_matte == MagickFalse || image_depth >= 8))
9967       {
9968         size_t one=1;
9969 
9970         if (image_matte != MagickFalse)
9971           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9972 
9973         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9974           {
9975             ping_color_type=PNG_COLOR_TYPE_GRAY;
9976 
9977             if (save_image_depth == 16 && image_depth == 8)
9978               {
9979                 if (logging != MagickFalse)
9980                   {
9981                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9982                         "  Scaling ping_trans_color (0)");
9983                   }
9984                     ping_trans_color.gray*=0x0101;
9985               }
9986           }
9987 
9988         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9989           image_depth=MAGICKCORE_QUANTUM_DEPTH;
9990 
9991         if ((image_colors == 0) ||
9992              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9993           image_colors=(int) (one << image_depth);
9994 
9995         if (image_depth > 8)
9996           ping_bit_depth=16;
9997 
9998         else
9999           {
10000             ping_bit_depth=8;
10001             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10002               {
10003                 if(!mng_info->write_png_depth)
10004                   {
10005                     ping_bit_depth=1;
10006 
10007                     while ((int) (one << ping_bit_depth)
10008                         < (ssize_t) image_colors)
10009                       ping_bit_depth <<= 1;
10010                   }
10011               }
10012 
10013             else if (ping_color_type ==
10014                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10015                 mng_info->IsPalette)
10016               {
10017               /* Check if grayscale is reducible */
10018 
10019                 int
10020                   depth_4_ok=MagickTrue,
10021                   depth_2_ok=MagickTrue,
10022                   depth_1_ok=MagickTrue;
10023 
10024                 for (i=0; i < (ssize_t) image_colors; i++)
10025                 {
10026                    unsigned char
10027                      intensity;
10028 
10029                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10030 
10031                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10032                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10033                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10034                      depth_2_ok=depth_1_ok=MagickFalse;
10035                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10036                      depth_1_ok=MagickFalse;
10037                 }
10038 
10039                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10040                   ping_bit_depth=1;
10041 
10042                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10043                   ping_bit_depth=2;
10044 
10045                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10046                   ping_bit_depth=4;
10047               }
10048           }
10049 
10050           image_depth=ping_bit_depth;
10051       }
10052 
10053     else
10054 
10055       if (mng_info->IsPalette)
10056       {
10057         number_colors=image_colors;
10058 
10059         if (image_depth <= 8)
10060           {
10061             /*
10062               Set image palette.
10063             */
10064             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10065 
10066             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10067               {
10068                 for (i=0; i < (ssize_t) number_colors; i++)
10069                 {
10070                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10071                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10072                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10073                 }
10074 
10075                 if (logging != MagickFalse)
10076                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10077                     "  Setting up PLTE chunk with %d colors",
10078                     number_colors);
10079 
10080                 ping_have_PLTE=MagickTrue;
10081               }
10082 
10083             /* color_type is PNG_COLOR_TYPE_PALETTE */
10084             if (mng_info->write_png_depth == 0)
10085               {
10086                 size_t
10087                   one;
10088 
10089                 ping_bit_depth=1;
10090                 one=1;
10091 
10092                 while ((one << ping_bit_depth) < (size_t) number_colors)
10093                   ping_bit_depth <<= 1;
10094               }
10095 
10096             ping_num_trans=0;
10097 
10098             if (matte != MagickFalse)
10099               {
10100                 /*
10101                  * Set up trans_colors array.
10102                  */
10103                 assert(number_colors <= 256);
10104 
10105                 ping_num_trans=(unsigned short) (number_transparent +
10106                   number_semitransparent);
10107 
10108                 if (ping_num_trans == 0)
10109                   ping_have_tRNS=MagickFalse;
10110 
10111                 else
10112                   {
10113                     if (logging != MagickFalse)
10114                       {
10115                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10116                           "  Scaling ping_trans_color (1)");
10117                       }
10118                     ping_have_tRNS=MagickTrue;
10119 
10120                     for (i=0; i < ping_num_trans; i++)
10121                     {
10122                        ping_trans_alpha[i]= (png_byte)
10123                          ScaleQuantumToChar(image->colormap[i].alpha);
10124                     }
10125                   }
10126               }
10127           }
10128       }
10129 
10130     else
10131       {
10132 
10133         if (image_depth < 8)
10134           image_depth=8;
10135 
10136         if ((save_image_depth == 16) && (image_depth == 8))
10137           {
10138             if (logging != MagickFalse)
10139               {
10140                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10141                   "    Scaling ping_trans_color from (%d,%d,%d)",
10142                   (int) ping_trans_color.red,
10143                   (int) ping_trans_color.green,
10144                   (int) ping_trans_color.blue);
10145               }
10146 
10147             ping_trans_color.red*=0x0101;
10148             ping_trans_color.green*=0x0101;
10149             ping_trans_color.blue*=0x0101;
10150             ping_trans_color.gray*=0x0101;
10151 
10152             if (logging != MagickFalse)
10153               {
10154                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10155                   "    to (%d,%d,%d)",
10156                   (int) ping_trans_color.red,
10157                   (int) ping_trans_color.green,
10158                   (int) ping_trans_color.blue);
10159               }
10160           }
10161       }
10162 
10163     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10164          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10165 
10166     /*
10167       Adjust background and transparency samples in sub-8-bit grayscale files.
10168     */
10169     if (ping_bit_depth < 8 && ping_color_type ==
10170         PNG_COLOR_TYPE_GRAY)
10171       {
10172          png_uint_16
10173            maxval;
10174 
10175          size_t
10176            one=1;
10177 
10178          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10179 
10180          if (ping_exclude_bKGD == MagickFalse)
10181          {
10182 
10183          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10184            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10185            &image->background_color))) +.5)));
10186 
10187          if (logging != MagickFalse)
10188            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10189              "  Setting up bKGD chunk (2)");
10190          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10191              "      background_color index is %d",
10192              (int) ping_background.index);
10193 
10194          ping_have_bKGD = MagickTrue;
10195          }
10196 
10197          if (logging != MagickFalse)
10198            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10199              "  Scaling ping_trans_color.gray from %d",
10200              (int)ping_trans_color.gray);
10201 
10202          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10203            ping_trans_color.gray)+.5);
10204 
10205          if (logging != MagickFalse)
10206            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10207              "      to %d", (int)ping_trans_color.gray);
10208       }
10209 
10210   if (ping_exclude_bKGD == MagickFalse)
10211   {
10212     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10213       {
10214         /*
10215            Identify which colormap entry is the background color.
10216         */
10217 
10218         number_colors=image_colors;
10219 
10220         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10221           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10222             break;
10223 
10224         ping_background.index=(png_byte) i;
10225 
10226         if (logging != MagickFalse)
10227           {
10228             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10229               "  Setting up bKGD chunk with index=%d",(int) i);
10230           }
10231 
10232         if (i < (ssize_t) number_colors)
10233           {
10234             ping_have_bKGD = MagickTrue;
10235 
10236             if (logging != MagickFalse)
10237               {
10238                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10239                   "     background   =(%d,%d,%d)",
10240                         (int) ping_background.red,
10241                         (int) ping_background.green,
10242                         (int) ping_background.blue);
10243               }
10244           }
10245 
10246         else  /* Can't happen */
10247           {
10248             if (logging != MagickFalse)
10249               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10250                   "      No room in PLTE to add bKGD color");
10251             ping_have_bKGD = MagickFalse;
10252           }
10253       }
10254   }
10255 
10256   if (logging != MagickFalse)
10257     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10258       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10259       ping_color_type);
10260   /*
10261     Initialize compression level and filtering.
10262   */
10263   if (logging != MagickFalse)
10264     {
10265       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10266         "  Setting up deflate compression");
10267 
10268       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10269         "    Compression buffer size: 32768");
10270     }
10271 
10272   png_set_compression_buffer_size(ping,32768L);
10273 
10274   if (logging != MagickFalse)
10275     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10276       "    Compression mem level: 9");
10277 
10278   png_set_compression_mem_level(ping, 9);
10279 
10280   /* Untangle the "-quality" setting:
10281 
10282      Undefined is 0; the default is used.
10283      Default is 75
10284 
10285      10's digit:
10286 
10287         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10288            zlib default compression level
10289 
10290         1-9: the zlib compression level
10291 
10292      1's digit:
10293 
10294         0-4: the PNG filter method
10295 
10296         5:   libpng adaptive filtering if compression level > 5
10297              libpng filter type "none" if compression level <= 5
10298                 or if image is grayscale or palette
10299 
10300         6:   libpng adaptive filtering
10301 
10302         7:   "LOCO" filtering (intrapixel differing) if writing
10303              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10304              and earlier because of a missing "else".
10305 
10306         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10307              filtering. Unused prior to IM-6.7.0-10, was same as 6
10308 
10309         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10310              Unused prior to IM-6.7.0-10, was same as 6
10311 
10312     Note that using the -quality option, not all combinations of
10313     PNG filter type, zlib compression level, and zlib compression
10314     strategy are possible.  This will be addressed soon in a
10315     release that accomodates "-define png:compression-strategy", etc.
10316 
10317    */
10318 
10319   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10320      image_info->quality;
10321 
10322   if (quality <= 9)
10323     {
10324       if (mng_info->write_png_compression_strategy == 0)
10325         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10326     }
10327 
10328   else if (mng_info->write_png_compression_level == 0)
10329     {
10330       int
10331         level;
10332 
10333       level=(int) MagickMin((ssize_t) quality/10,9);
10334 
10335       mng_info->write_png_compression_level = level+1;
10336     }
10337 
10338   if (mng_info->write_png_compression_strategy == 0)
10339     {
10340         if ((quality %10) == 8 || (quality %10) == 9)
10341 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10342           mng_info->write_png_compression_strategy=Z_RLE+1;
10343 #else
10344           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10345 #endif
10346     }
10347 
10348   if (mng_info->write_png_compression_filter == 0)
10349         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10350 
10351   if (logging != MagickFalse)
10352     {
10353      if (mng_info->write_png_compression_level)
10354         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10355           "    Compression level:    %d",
10356             (int) mng_info->write_png_compression_level-1);
10357 
10358      if (mng_info->write_png_compression_strategy)
10359         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10360           "    Compression strategy: %d",
10361             (int) mng_info->write_png_compression_strategy-1);
10362 
10363         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10364           "  Setting up filtering");
10365 
10366         if (mng_info->write_png_compression_filter == 6)
10367           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10368             "    Base filter method: ADAPTIVE");
10369         else if (mng_info->write_png_compression_filter == 0 ||
10370                  mng_info->write_png_compression_filter == 1)
10371           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10372             "    Base filter method: NONE");
10373         else
10374           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10375             "    Base filter method: %d",
10376             (int) mng_info->write_png_compression_filter-1);
10377     }
10378 
10379   if (mng_info->write_png_compression_level != 0)
10380     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10381 
10382   if (mng_info->write_png_compression_filter == 6)
10383     {
10384       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10385          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10386          (quality < 50))
10387         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10388       else
10389         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10390      }
10391   else if (mng_info->write_png_compression_filter == 7 ||
10392       mng_info->write_png_compression_filter == 10)
10393     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10394 
10395   else if (mng_info->write_png_compression_filter == 8)
10396     {
10397 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10398       if (mng_info->write_mng)
10399       {
10400          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10401              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10402         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10403       }
10404 #endif
10405       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10406     }
10407 
10408   else if (mng_info->write_png_compression_filter == 9)
10409     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10410 
10411   else if (mng_info->write_png_compression_filter != 0)
10412     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10413        mng_info->write_png_compression_filter-1);
10414 
10415   if (mng_info->write_png_compression_strategy != 0)
10416     png_set_compression_strategy(ping,
10417        mng_info->write_png_compression_strategy-1);
10418 
10419   ping_interlace_method=image_info->interlace != NoInterlace;
10420 
10421   if (mng_info->write_mng)
10422     png_set_sig_bytes(ping,8);
10423 
10424   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10425 
10426   if (mng_info->write_png_colortype != 0)
10427     {
10428      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10429        if (ping_have_color != MagickFalse)
10430          {
10431            ping_color_type = PNG_COLOR_TYPE_RGB;
10432 
10433            if (ping_bit_depth < 8)
10434              ping_bit_depth=8;
10435          }
10436 
10437      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10438        if (ping_have_color != MagickFalse)
10439          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10440     }
10441 
10442   if (ping_need_colortype_warning != MagickFalse ||
10443      ((mng_info->write_png_depth &&
10444      (int) mng_info->write_png_depth != ping_bit_depth) ||
10445      (mng_info->write_png_colortype &&
10446      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10447       mng_info->write_png_colortype != 7 &&
10448       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10449     {
10450       if (logging != MagickFalse)
10451         {
10452           if (ping_need_colortype_warning != MagickFalse)
10453             {
10454               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455                  "  Image has transparency but tRNS chunk was excluded");
10456             }
10457 
10458           if (mng_info->write_png_depth)
10459             {
10460               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10461                   "  Defined png:bit-depth=%u, Computed depth=%u",
10462                   mng_info->write_png_depth,
10463                   ping_bit_depth);
10464             }
10465 
10466           if (mng_info->write_png_colortype)
10467             {
10468               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10469                   "  Defined png:color-type=%u, Computed color type=%u",
10470                   mng_info->write_png_colortype-1,
10471                   ping_color_type);
10472             }
10473         }
10474 
10475       png_warning(ping,
10476         "Cannot write image with defined png:bit-depth or png:color-type.");
10477     }
10478 
10479   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10480     {
10481       /* Add an opaque matte channel */
10482       image->alpha_trait = BlendPixelTrait;
10483       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10484 
10485       if (logging != MagickFalse)
10486         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10487           "  Added an opaque matte channel");
10488     }
10489 
10490   if (number_transparent != 0 || number_semitransparent != 0)
10491     {
10492       if (ping_color_type < 4)
10493         {
10494            ping_have_tRNS=MagickTrue;
10495            if (logging != MagickFalse)
10496              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10497                "  Setting ping_have_tRNS=MagickTrue.");
10498         }
10499     }
10500 
10501   if (logging != MagickFalse)
10502     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10503       "  Writing PNG header chunks");
10504 
10505   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10506                ping_bit_depth,ping_color_type,
10507                ping_interlace_method,ping_compression_method,
10508                ping_filter_method);
10509 
10510   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10511     {
10512       png_set_PLTE(ping,ping_info,palette,number_colors);
10513 
10514       if (logging != MagickFalse)
10515         {
10516           for (i=0; i< (ssize_t) number_colors; i++)
10517           {
10518             if (i < ping_num_trans)
10519               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10520                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10521                       (int) i,
10522                       (int) palette[i].red,
10523                       (int) palette[i].green,
10524                       (int) palette[i].blue,
10525                       (int) i,
10526                       (int) ping_trans_alpha[i]);
10527              else
10528               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10529                 "     PLTE[%d] = (%d,%d,%d)",
10530                       (int) i,
10531                       (int) palette[i].red,
10532                       (int) palette[i].green,
10533                       (int) palette[i].blue);
10534            }
10535          }
10536     }
10537 
10538   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10539   if (ping_exclude_sRGB != MagickFalse ||
10540      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10541   {
10542     if ((ping_exclude_tEXt == MagickFalse ||
10543        ping_exclude_zTXt == MagickFalse) &&
10544        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10545     {
10546       ResetImageProfileIterator(image);
10547       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10548       {
10549         profile=GetImageProfile(image,name);
10550 
10551         if (profile != (StringInfo *) NULL)
10552           {
10553 #ifdef PNG_WRITE_iCCP_SUPPORTED
10554             if ((LocaleCompare(name,"ICC") == 0) ||
10555                 (LocaleCompare(name,"ICM") == 0))
10556              {
10557 
10558                if (ping_exclude_iCCP == MagickFalse)
10559                  {
10560                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10561                           "  Setting up iCCP chunk");
10562 
10563                        png_set_iCCP(ping,ping_info,(png_charp) name,0,
10564 #if (PNG_LIBPNG_VER < 10500)
10565                          (png_charp) GetStringInfoDatum(profile),
10566 #else
10567                          (const png_byte *) GetStringInfoDatum(profile),
10568 #endif
10569                          (png_uint_32) GetStringInfoLength(profile));
10570                        ping_have_iCCP = MagickTrue;
10571                  }
10572              }
10573 
10574             else
10575 #endif
10576               if (ping_exclude_zCCP == MagickFalse)
10577                 {
10578                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10579                       "  Setting up zTXT chunk with uuencoded ICC");
10580                   Magick_png_write_raw_profile(image_info,ping,ping_info,
10581                     (unsigned char *) name,(unsigned char *) name,
10582                     GetStringInfoDatum(profile),
10583                     (png_uint_32) GetStringInfoLength(profile));
10584                   ping_have_iCCP = MagickTrue;
10585                 }
10586           }
10587 
10588           if (logging != MagickFalse)
10589             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10590               "  Setting up text chunk with %s profile",name);
10591 
10592         name=GetNextImageProfile(image);
10593       }
10594     }
10595   }
10596 
10597 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10598   if ((mng_info->have_write_global_srgb == 0) &&
10599       ping_have_iCCP != MagickTrue &&
10600       (ping_have_sRGB != MagickFalse ||
10601       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10602     {
10603       if (ping_exclude_sRGB == MagickFalse)
10604         {
10605           /*
10606             Note image rendering intent.
10607           */
10608           if (logging != MagickFalse)
10609             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10610                 "  Setting up sRGB chunk");
10611 
10612           (void) png_set_sRGB(ping,ping_info,(
10613             Magick_RenderingIntent_to_PNG_RenderingIntent(
10614               image->rendering_intent)));
10615 
10616           ping_have_sRGB = MagickTrue;
10617         }
10618     }
10619 
10620   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10621 #endif
10622     {
10623       if (ping_exclude_gAMA == MagickFalse &&
10624           ping_have_iCCP == MagickFalse &&
10625           ping_have_sRGB == MagickFalse &&
10626           (ping_exclude_sRGB == MagickFalse ||
10627           (image->gamma < .45 || image->gamma > .46)))
10628       {
10629       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10630         {
10631           /*
10632             Note image gamma.
10633             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10634           */
10635           if (logging != MagickFalse)
10636             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10637               "  Setting up gAMA chunk");
10638 
10639           png_set_gAMA(ping,ping_info,image->gamma);
10640         }
10641       }
10642 
10643       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10644         {
10645           if ((mng_info->have_write_global_chrm == 0) &&
10646               (image->chromaticity.red_primary.x != 0.0))
10647             {
10648               /*
10649                 Note image chromaticity.
10650                 Note: if cHRM+gAMA == sRGB write sRGB instead.
10651               */
10652                PrimaryInfo
10653                  bp,
10654                  gp,
10655                  rp,
10656                  wp;
10657 
10658                wp=image->chromaticity.white_point;
10659                rp=image->chromaticity.red_primary;
10660                gp=image->chromaticity.green_primary;
10661                bp=image->chromaticity.blue_primary;
10662 
10663                if (logging != MagickFalse)
10664                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10665                    "  Setting up cHRM chunk");
10666 
10667                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10668                    bp.x,bp.y);
10669            }
10670         }
10671     }
10672 
10673   if (ping_exclude_bKGD == MagickFalse)
10674     {
10675       if (ping_have_bKGD != MagickFalse)
10676         {
10677           png_set_bKGD(ping,ping_info,&ping_background);
10678           if (logging != MagickFalse)
10679             {
10680               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10681                    "    Setting up bKGD chunk");
10682               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10683                    "      background color = (%d,%d,%d)",
10684                         (int) ping_background.red,
10685                         (int) ping_background.green,
10686                         (int) ping_background.blue);
10687               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10688                    "      index = %d, gray=%d",
10689                         (int) ping_background.index,
10690                         (int) ping_background.gray);
10691             }
10692          }
10693     }
10694 
10695   if (ping_exclude_pHYs == MagickFalse)
10696     {
10697       if (ping_have_pHYs != MagickFalse)
10698         {
10699           png_set_pHYs(ping,ping_info,
10700              ping_pHYs_x_resolution,
10701              ping_pHYs_y_resolution,
10702              ping_pHYs_unit_type);
10703 
10704           if (logging != MagickFalse)
10705             {
10706               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10707                    "    Setting up pHYs chunk");
10708               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10709                    "      x_resolution=%lu",
10710                    (unsigned long) ping_pHYs_x_resolution);
10711               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10712                    "      y_resolution=%lu",
10713                    (unsigned long) ping_pHYs_y_resolution);
10714               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10715                    "      unit_type=%lu",
10716                    (unsigned long) ping_pHYs_unit_type);
10717             }
10718         }
10719     }
10720 
10721 #if defined(PNG_oFFs_SUPPORTED)
10722   if (ping_exclude_oFFs == MagickFalse)
10723     {
10724       if (image->page.x || image->page.y)
10725         {
10726            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10727               (png_int_32) image->page.y, 0);
10728 
10729            if (logging != MagickFalse)
10730              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10731                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10732                  (int) image->page.x, (int) image->page.y);
10733         }
10734     }
10735 #endif
10736 
10737 #if defined(PNG_tIME_SUPPORTED)
10738   if (ping_exclude_tIME == MagickFalse)
10739     {
10740       const char
10741         *timestamp;
10742 
10743       if (image->taint == MagickFalse)
10744         {
10745           timestamp=GetImageOption(image_info,"png:tIME");
10746 
10747           if (timestamp == (const char *) NULL)
10748             timestamp=GetImageProperty(image,"png:tIME",exception);
10749         }
10750 
10751       else
10752         {
10753           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10754              "  Reset tIME in tainted image");
10755 
10756           timestamp=GetImageProperty(image,"date:modify",exception);
10757         }
10758 
10759       if (timestamp != (const char *) NULL)
10760           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10761     }
10762 #endif
10763 
10764   if (mng_info->need_blob != MagickFalse)
10765   {
10766     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10767        MagickFalse)
10768        png_error(ping,"WriteBlob Failed");
10769 
10770      ping_have_blob=MagickTrue;
10771   }
10772 
10773   png_write_info_before_PLTE(ping, ping_info);
10774 
10775   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10776     {
10777       if (logging != MagickFalse)
10778         {
10779           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10780               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10781         }
10782 
10783       if (ping_color_type == 3)
10784          (void) png_set_tRNS(ping, ping_info,
10785                 ping_trans_alpha,
10786                 ping_num_trans,
10787                 NULL);
10788 
10789       else
10790         {
10791            (void) png_set_tRNS(ping, ping_info,
10792                   NULL,
10793                   0,
10794                   &ping_trans_color);
10795 
10796            if (logging != MagickFalse)
10797              {
10798                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10799                  "     tRNS color   =(%d,%d,%d)",
10800                        (int) ping_trans_color.red,
10801                        (int) ping_trans_color.green,
10802                        (int) ping_trans_color.blue);
10803              }
10804          }
10805     }
10806 
10807   /* write any png-chunk-b profiles */
10808   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10809 
10810   png_write_info(ping,ping_info);
10811 
10812   /* write any PNG-chunk-m profiles */
10813   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10814 
10815   if (ping_exclude_vpAg == MagickFalse)
10816     {
10817       if ((image->page.width != 0 && image->page.width != image->columns) ||
10818           (image->page.height != 0 && image->page.height != image->rows))
10819         {
10820           unsigned char
10821             chunk[14];
10822 
10823           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10824           PNGType(chunk,mng_vpAg);
10825           LogPNGChunk(logging,mng_vpAg,9L);
10826           PNGLong(chunk+4,(png_uint_32) image->page.width);
10827           PNGLong(chunk+8,(png_uint_32) image->page.height);
10828           chunk[12]=0;   /* unit = pixels */
10829           (void) WriteBlob(image,13,chunk);
10830           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10831         }
10832     }
10833 
10834 #if (PNG_LIBPNG_VER == 10206)
10835     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10836 #define PNG_HAVE_IDAT               0x04
10837     ping->mode |= PNG_HAVE_IDAT;
10838 #undef PNG_HAVE_IDAT
10839 #endif
10840 
10841   png_set_packing(ping);
10842   /*
10843     Allocate memory.
10844   */
10845   rowbytes=image->columns;
10846   if (image_depth > 8)
10847     rowbytes*=2;
10848   switch (ping_color_type)
10849     {
10850       case PNG_COLOR_TYPE_RGB:
10851         rowbytes*=3;
10852         break;
10853 
10854       case PNG_COLOR_TYPE_GRAY_ALPHA:
10855         rowbytes*=2;
10856         break;
10857 
10858       case PNG_COLOR_TYPE_RGBA:
10859         rowbytes*=4;
10860         break;
10861 
10862       default:
10863         break;
10864     }
10865 
10866   if (logging != MagickFalse)
10867     {
10868       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10869         "  Writing PNG image data");
10870 
10871       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10872         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10873     }
10874   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10875   if (pixel_info == (MemoryInfo *) NULL)
10876     png_error(ping,"Allocation of memory for pixels failed");
10877   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10878 
10879   /*
10880     Initialize image scanlines.
10881   */
10882   quantum_info=AcquireQuantumInfo(image_info,image);
10883   if (quantum_info == (QuantumInfo *) NULL)
10884     png_error(ping,"Memory allocation for quantum_info failed");
10885   quantum_info->format=UndefinedQuantumFormat;
10886   SetQuantumDepth(image,quantum_info,image_depth);
10887   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10888   num_passes=png_set_interlace_handling(ping);
10889 
10890   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10891        !mng_info->write_png48 && !mng_info->write_png64 &&
10892        !mng_info->write_png32) &&
10893        (mng_info->IsPalette ||
10894        (image_info->type == BilevelType)) &&
10895        image_matte == MagickFalse &&
10896        ping_have_non_bw == MagickFalse)
10897     {
10898       /* Palette, Bilevel, or Opaque Monochrome */
10899       register const Quantum
10900         *p;
10901 
10902       SetQuantumDepth(image,quantum_info,8);
10903       for (pass=0; pass < num_passes; pass++)
10904       {
10905         /*
10906           Convert PseudoClass image to a PNG monochrome image.
10907         */
10908         for (y=0; y < (ssize_t) image->rows; y++)
10909         {
10910           if (logging != MagickFalse && y == 0)
10911              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10912                  "    Writing row of pixels (0)");
10913 
10914           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10915 
10916           if (p == (const Quantum *) NULL)
10917             break;
10918 
10919           if (mng_info->IsPalette)
10920             {
10921               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10922                 quantum_info,GrayQuantum,ping_pixels,exception);
10923               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10924                   mng_info->write_png_depth &&
10925                   mng_info->write_png_depth != old_bit_depth)
10926                 {
10927                   /* Undo pixel scaling */
10928                   for (i=0; i < (ssize_t) image->columns; i++)
10929                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10930                      >> (8-old_bit_depth));
10931                 }
10932             }
10933 
10934           else
10935             {
10936               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10937                 quantum_info,RedQuantum,ping_pixels,exception);
10938             }
10939 
10940           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10941             for (i=0; i < (ssize_t) image->columns; i++)
10942                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10943                       255 : 0);
10944 
10945           if (logging != MagickFalse && y == 0)
10946             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10947                 "    Writing row of pixels (1)");
10948 
10949           png_write_row(ping,ping_pixels);
10950 
10951           status=SetImageProgress(image,SaveImageTag,
10952               (MagickOffsetType) (pass * image->rows + y),
10953               num_passes * image->rows);
10954 
10955           if (status == MagickFalse)
10956             break;
10957         }
10958       }
10959     }
10960 
10961   else   /* Not Palette, Bilevel, or Opaque Monochrome */
10962     {
10963       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10964           !mng_info->write_png48 && !mng_info->write_png64 &&
10965           !mng_info->write_png32) && (image_matte != MagickFalse ||
10966           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10967           (mng_info->IsPalette) && ping_have_color == MagickFalse)
10968         {
10969           register const Quantum
10970             *p;
10971 
10972           for (pass=0; pass < num_passes; pass++)
10973           {
10974 
10975           for (y=0; y < (ssize_t) image->rows; y++)
10976           {
10977             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10978 
10979             if (p == (const Quantum *) NULL)
10980               break;
10981 
10982             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10983               {
10984                 if (mng_info->IsPalette)
10985                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10986                     quantum_info,GrayQuantum,ping_pixels,exception);
10987 
10988                 else
10989                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10990                     quantum_info,RedQuantum,ping_pixels,exception);
10991 
10992                 if (logging != MagickFalse && y == 0)
10993                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10994                        "    Writing GRAY PNG pixels (2)");
10995               }
10996 
10997             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10998               {
10999                 if (logging != MagickFalse && y == 0)
11000                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11001                          "    Writing GRAY_ALPHA PNG pixels (2)");
11002 
11003                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11004                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11005               }
11006 
11007             if (logging != MagickFalse && y == 0)
11008               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11009                   "    Writing row of pixels (2)");
11010 
11011             png_write_row(ping,ping_pixels);
11012 
11013             status=SetImageProgress(image,SaveImageTag,
11014               (MagickOffsetType) (pass * image->rows + y),
11015               num_passes * image->rows);
11016 
11017             if (status == MagickFalse)
11018               break;
11019             }
11020           }
11021         }
11022 
11023       else
11024         {
11025           register const Quantum
11026             *p;
11027 
11028           for (pass=0; pass < num_passes; pass++)
11029           {
11030             if ((image_depth > 8) ||
11031                 mng_info->write_png24 ||
11032                 mng_info->write_png32 ||
11033                 mng_info->write_png48 ||
11034                 mng_info->write_png64 ||
11035                 (!mng_info->write_png8 && !mng_info->IsPalette))
11036             {
11037               for (y=0; y < (ssize_t) image->rows; y++)
11038               {
11039                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11040 
11041                 if (p == (const Quantum *) NULL)
11042                   break;
11043 
11044                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11045                   {
11046                     if (image->storage_class == DirectClass)
11047                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11048                         quantum_info,RedQuantum,ping_pixels,exception);
11049 
11050                     else
11051                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11052                         quantum_info,GrayQuantum,ping_pixels,exception);
11053                   }
11054 
11055                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11056                   {
11057                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11058                       quantum_info,GrayAlphaQuantum,ping_pixels,
11059                       exception);
11060 
11061                     if (logging != MagickFalse && y == 0)
11062                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063                            "    Writing GRAY_ALPHA PNG pixels (3)");
11064                   }
11065 
11066                 else if (image_matte != MagickFalse)
11067                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11068                     quantum_info,RGBAQuantum,ping_pixels,exception);
11069 
11070                 else
11071                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11072                     quantum_info,RGBQuantum,ping_pixels,exception);
11073 
11074                 if (logging != MagickFalse && y == 0)
11075                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11076                       "    Writing row of pixels (3)");
11077 
11078                 png_write_row(ping,ping_pixels);
11079 
11080                 status=SetImageProgress(image,SaveImageTag,
11081                   (MagickOffsetType) (pass * image->rows + y),
11082                   num_passes * image->rows);
11083 
11084                 if (status == MagickFalse)
11085                   break;
11086               }
11087             }
11088 
11089           else
11090             /* not ((image_depth > 8) ||
11091                 mng_info->write_png24 || mng_info->write_png32 ||
11092                 mng_info->write_png48 || mng_info->write_png64 ||
11093                 (!mng_info->write_png8 && !mng_info->IsPalette))
11094              */
11095             {
11096               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11097                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11098                 {
11099                   if (logging != MagickFalse)
11100                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11101                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11102 
11103                   SetQuantumDepth(image,quantum_info,8);
11104                   image_depth=8;
11105                 }
11106 
11107               for (y=0; y < (ssize_t) image->rows; y++)
11108               {
11109                 if (logging != MagickFalse && y == 0)
11110                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11111                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11112 
11113                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11114 
11115                 if (p == (const Quantum *) NULL)
11116                   break;
11117 
11118                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11119                   {
11120                     SetQuantumDepth(image,quantum_info,image->depth);
11121 
11122                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11123                        quantum_info,GrayQuantum,ping_pixels,exception);
11124                   }
11125 
11126                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11127                   {
11128                     if (logging != MagickFalse && y == 0)
11129                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11130                            "  Writing GRAY_ALPHA PNG pixels (4)");
11131 
11132                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11133                          quantum_info,GrayAlphaQuantum,ping_pixels,
11134                          exception);
11135                   }
11136 
11137                 else
11138                   {
11139                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11140                       quantum_info,IndexQuantum,ping_pixels,exception);
11141 
11142                     if (logging != MagickFalse && y <= 2)
11143                     {
11144                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11145                           "  Writing row of non-gray pixels (4)");
11146 
11147                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11148                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11149                           (int)ping_pixels[0],(int)ping_pixels[1]);
11150                     }
11151                   }
11152                 png_write_row(ping,ping_pixels);
11153 
11154                 status=SetImageProgress(image,SaveImageTag,
11155                   (MagickOffsetType) (pass * image->rows + y),
11156                   num_passes * image->rows);
11157 
11158                 if (status == MagickFalse)
11159                   break;
11160               }
11161             }
11162           }
11163         }
11164     }
11165 
11166   if (quantum_info != (QuantumInfo *) NULL)
11167     quantum_info=DestroyQuantumInfo(quantum_info);
11168 
11169   if (logging != MagickFalse)
11170     {
11171       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11172         "  Wrote PNG image data");
11173 
11174       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11175         "    Width: %.20g",(double) ping_width);
11176 
11177       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11178         "    Height: %.20g",(double) ping_height);
11179 
11180       if (mng_info->write_png_depth)
11181         {
11182           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11183             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11184         }
11185 
11186       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11187         "    PNG bit-depth written: %d",ping_bit_depth);
11188 
11189       if (mng_info->write_png_colortype)
11190         {
11191           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11192             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11193         }
11194 
11195       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11196         "    PNG color-type written: %d",ping_color_type);
11197 
11198       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11199         "    PNG Interlace method: %d",ping_interlace_method);
11200     }
11201   /*
11202     Generate text chunks after IDAT.
11203   */
11204   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11205   {
11206     ResetImagePropertyIterator(image);
11207     property=GetNextImageProperty(image);
11208     while (property != (const char *) NULL)
11209     {
11210       png_textp
11211         text;
11212 
11213       value=GetImageProperty(image,property,exception);
11214 
11215       /* Don't write any "png:" or "jpeg:" properties; those are just for
11216        * "identify" or for passing through to another JPEG
11217        */
11218       if ((LocaleNCompare(property,"png:",4) != 0 &&
11219            LocaleNCompare(property,"jpeg:",5) != 0) &&
11220 
11221 
11222           /* Suppress density and units if we wrote a pHYs chunk */
11223           (ping_exclude_pHYs != MagickFalse      ||
11224           LocaleCompare(property,"density") != 0 ||
11225           LocaleCompare(property,"units") != 0) &&
11226 
11227           /* Suppress the IM-generated Date:create and Date:modify */
11228           (ping_exclude_date == MagickFalse      ||
11229           LocaleNCompare(property, "Date:",5) != 0))
11230         {
11231         if (value != (const char *) NULL)
11232           {
11233 
11234 #if PNG_LIBPNG_VER >= 10400
11235             text=(png_textp) png_malloc(ping,
11236                  (png_alloc_size_t) sizeof(png_text));
11237 #else
11238             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11239 #endif
11240             text[0].key=(char *) property;
11241             text[0].text=(char *) value;
11242             text[0].text_length=strlen(value);
11243 
11244             if (ping_exclude_tEXt != MagickFalse)
11245                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11246 
11247             else if (ping_exclude_zTXt != MagickFalse)
11248                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11249 
11250             else
11251             {
11252                text[0].compression=image_info->compression == NoCompression ||
11253                  (image_info->compression == UndefinedCompression &&
11254                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11255                  PNG_TEXT_COMPRESSION_zTXt ;
11256             }
11257 
11258             if (logging != MagickFalse)
11259               {
11260                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11261                   "  Setting up text chunk");
11262 
11263                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11264                   "    keyword: '%s'",text[0].key);
11265               }
11266 
11267             png_set_text(ping,ping_info,text,1);
11268             png_free(ping,text);
11269           }
11270         }
11271       property=GetNextImageProperty(image);
11272     }
11273   }
11274 
11275   /* write any PNG-chunk-e profiles */
11276   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11277 
11278   if (logging != MagickFalse)
11279     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11280       "  Writing PNG end info");
11281 
11282   png_write_end(ping,ping_info);
11283 
11284   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11285     {
11286       if (mng_info->page.x || mng_info->page.y ||
11287           (ping_width != mng_info->page.width) ||
11288           (ping_height != mng_info->page.height))
11289         {
11290           unsigned char
11291             chunk[32];
11292 
11293           /*
11294             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11295           */
11296           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11297           PNGType(chunk,mng_FRAM);
11298           LogPNGChunk(logging,mng_FRAM,27L);
11299           chunk[4]=4;
11300           chunk[5]=0;  /* frame name separator (no name) */
11301           chunk[6]=1;  /* flag for changing delay, for next frame only */
11302           chunk[7]=0;  /* flag for changing frame timeout */
11303           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11304           chunk[9]=0;  /* flag for changing frame sync_id */
11305           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11306           chunk[14]=0; /* clipping boundaries delta type */
11307           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11308           PNGLong(chunk+19,
11309              (png_uint_32) (mng_info->page.x + ping_width));
11310           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11311           PNGLong(chunk+27,
11312              (png_uint_32) (mng_info->page.y + ping_height));
11313           (void) WriteBlob(image,31,chunk);
11314           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11315           mng_info->old_framing_mode=4;
11316           mng_info->framing_mode=1;
11317         }
11318 
11319       else
11320         mng_info->framing_mode=3;
11321     }
11322   if (mng_info->write_mng && !mng_info->need_fram &&
11323       ((int) image->dispose == 3))
11324      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11325 
11326   /*
11327     Free PNG resources.
11328   */
11329 
11330   png_destroy_write_struct(&ping,&ping_info);
11331 
11332   pixel_info=RelinquishVirtualMemory(pixel_info);
11333 
11334   if (ping_have_blob != MagickFalse)
11335      (void) CloseBlob(image);
11336 
11337   image_info=DestroyImageInfo(image_info);
11338   image=DestroyImage(image);
11339 
11340   /* Store bit depth actually written */
11341   s[0]=(char) ping_bit_depth;
11342   s[1]='\0';
11343 
11344   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11345 
11346   if (logging != MagickFalse)
11347     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11348       "  exit WriteOnePNGImage()");
11349 
11350 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11351   UnlockSemaphoreInfo(ping_semaphore);
11352 #endif
11353 
11354    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11355     *    Throwing an Exception when an error occurs.
11356     */
11357 
11358   return(MagickTrue);
11359 /*  End write one PNG image */
11360 
11361 }
11362 
11363 /*
11364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11365 %                                                                             %
11366 %                                                                             %
11367 %                                                                             %
11368 %   W r i t e P N G I m a g e                                                 %
11369 %                                                                             %
11370 %                                                                             %
11371 %                                                                             %
11372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11373 %
11374 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11375 %  Multiple-image Network Graphics (MNG) image file.
11376 %
11377 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11378 %
11379 %  The format of the WritePNGImage method is:
11380 %
11381 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11382 %        Image *image,ExceptionInfo *exception)
11383 %
11384 %  A description of each parameter follows:
11385 %
11386 %    o image_info: the image info.
11387 %
11388 %    o image:  The image.
11389 %
11390 %    o exception: return any errors or warnings in this structure.
11391 %
11392 %  Returns MagickTrue on success, MagickFalse on failure.
11393 %
11394 %  Communicating with the PNG encoder:
11395 %
11396 %  While the datastream written is always in PNG format and normally would
11397 %  be given the "png" file extension, this method also writes the following
11398 %  pseudo-formats which are subsets of png:
11399 %
11400 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11401 %               a depth greater than 8, the depth is reduced. If transparency
11402 %               is present, the tRNS chunk must only have values 0 and 255
11403 %               (i.e., transparency is binary: fully opaque or fully
11404 %               transparent).  If other values are present they will be
11405 %               50%-thresholded to binary transparency.  If more than 256
11406 %               colors are present, they will be quantized to the 4-4-4-1,
11407 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11408 %               of any resulting fully-transparent pixels is changed to
11409 %               the image's background color.
11410 %
11411 %               If you want better quantization or dithering of the colors
11412 %               or alpha than that, you need to do it before calling the
11413 %               PNG encoder. The pixels contain 8-bit indices even if
11414 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11415 %               images will be written as indexed PNG files even though the
11416 %               PNG grayscale type might be slightly more efficient.  Please
11417 %               note that writing to the PNG8 format may result in loss
11418 %               of color and alpha data.
11419 %
11420 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11421 %               chunk can be present to convey binary transparency by naming
11422 %               one of the colors as transparent.  The only loss incurred
11423 %               is reduction of sample depth to 8.  If the image has more
11424 %               than one transparent color, has semitransparent pixels, or
11425 %               has an opaque pixel with the same RGB components as the
11426 %               transparent color, an image is not written.
11427 %
11428 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11429 %               transparency is permitted, i.e., the alpha sample for
11430 %               each pixel can have any value from 0 to 255. The alpha
11431 %               channel is present even if the image is fully opaque.
11432 %               The only loss in data is the reduction of the sample depth
11433 %               to 8.
11434 %
11435 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11436 %               chunk can be present to convey binary transparency by naming
11437 %               one of the colors as transparent.  If the image has more
11438 %               than one transparent color, has semitransparent pixels, or
11439 %               has an opaque pixel with the same RGB components as the
11440 %               transparent color, an image is not written.
11441 %
11442 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11443 %               transparency is permitted, i.e., the alpha sample for
11444 %               each pixel can have any value from 0 to 65535. The alpha
11445 %               channel is present even if the image is fully opaque.
11446 %
11447 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11448 %               image, if the input was a PNG, is written.  If these values
11449 %               cannot be found, or if the pixels have been changed in a way
11450 %               that makes this impossible, then "PNG00" falls back to the
11451 %               regular "PNG" format.
11452 %
11453 %    o -define: For more precise control of the PNG output, you can use the
11454 %               Image options "png:bit-depth" and "png:color-type".  These
11455 %               can be set from the commandline with "-define" and also
11456 %               from the application programming interfaces.  The options
11457 %               are case-independent and are converted to lowercase before
11458 %               being passed to this encoder.
11459 %
11460 %               png:color-type can be 0, 2, 3, 4, or 6.
11461 %
11462 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11463 %               be 1, 2, 4, 8, or 16.
11464 %
11465 %               When png:color-type is 2 (RGB), png:bit-depth can
11466 %               be 8 or 16.
11467 %
11468 %               When png:color-type is 3 (Indexed), png:bit-depth can
11469 %               be 1, 2, 4, or 8.  This refers to the number of bits
11470 %               used to store the index.  The color samples always have
11471 %               bit-depth 8 in indexed PNG files.
11472 %
11473 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11474 %               png:bit-depth can be 8 or 16.
11475 %
11476 %               If the image cannot be written without loss with the
11477 %               requested bit-depth and color-type, a PNG file will not
11478 %               be written, a warning will be issued, and the encoder will
11479 %               return MagickFalse.
11480 %
11481 %  Since image encoders should not be responsible for the "heavy lifting",
11482 %  the user should make sure that ImageMagick has already reduced the
11483 %  image depth and number of colors and limit transparency to binary
11484 %  transparency prior to attempting to write the image with depth, color,
11485 %  or transparency limitations.
11486 %
11487 %  Note that another definition, "png:bit-depth-written" exists, but it
11488 %  is not intended for external use.  It is only used internally by the
11489 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11490 %
11491 %  It is possible to request that the PNG encoder write previously-formatted
11492 %  ancillary chunks in the output PNG file, using the "-profile" commandline
11493 %  option as shown below or by setting the profile via a programming
11494 %  interface:
11495 %
11496 %     -profile PNG-chunk-x:<file>
11497 %
11498 %  where x is a location flag and <file> is a file containing the chunk
11499 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11500 %  This encoder will compute the chunk length and CRC, so those must not
11501 %  be included in the file.
11502 %
11503 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11504 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
11505 %  of the same type, then add a short unique string after the "x" to prevent
11506 %  subsequent profiles from overwriting the preceding ones, e.g.,
11507 %
11508 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11509 %
11510 %  As of version 6.6.6 the following optimizations are always done:
11511 %
11512 %   o  32-bit depth is reduced to 16.
11513 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11514 %      high byte and low byte are identical.
11515 %   o  Palette is sorted to remove unused entries and to put a
11516 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11517 %   o  Opaque matte channel is removed when writing an indexed PNG.
11518 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11519 %      this can be done without loss and a larger bit depth N was not
11520 %      requested via the "-define png:bit-depth=N" option.
11521 %   o  If matte channel is present but only one transparent color is
11522 %      present, RGB+tRNS is written instead of RGBA
11523 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11524 %      was requested when converting an opaque image).
11525 %
11526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11527 */
WritePNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)11528 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11529   Image *image,ExceptionInfo *exception)
11530 {
11531   MagickBooleanType
11532     excluding,
11533     logging,
11534     have_mng_structure,
11535     status;
11536 
11537   MngInfo
11538     *mng_info;
11539 
11540   const char
11541     *value;
11542 
11543   int
11544     source;
11545 
11546   /*
11547     Open image file.
11548   */
11549   assert(image_info != (const ImageInfo *) NULL);
11550   assert(image_info->signature == MagickCoreSignature);
11551   assert(image != (Image *) NULL);
11552   assert(image->signature == MagickCoreSignature);
11553   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11554   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11555   /*
11556     Allocate a MngInfo structure.
11557   */
11558   have_mng_structure=MagickFalse;
11559   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11560 
11561   if (mng_info == (MngInfo *) NULL)
11562     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11563 
11564   /*
11565     Initialize members of the MngInfo structure.
11566   */
11567   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11568   mng_info->image=image;
11569   mng_info->equal_backgrounds=MagickTrue;
11570   have_mng_structure=MagickTrue;
11571 
11572   /* See if user has requested a specific PNG subformat */
11573 
11574   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11575   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11576   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11577   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11578   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11579 
11580   value=GetImageOption(image_info,"png:format");
11581 
11582   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11583     {
11584       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11585          "  Format=%s",value);
11586 
11587       mng_info->write_png8 = MagickFalse;
11588       mng_info->write_png24 = MagickFalse;
11589       mng_info->write_png32 = MagickFalse;
11590       mng_info->write_png48 = MagickFalse;
11591       mng_info->write_png64 = MagickFalse;
11592 
11593       if (LocaleCompare(value,"png8") == 0)
11594         mng_info->write_png8 = MagickTrue;
11595 
11596       else if (LocaleCompare(value,"png24") == 0)
11597         mng_info->write_png24 = MagickTrue;
11598 
11599       else if (LocaleCompare(value,"png32") == 0)
11600         mng_info->write_png32 = MagickTrue;
11601 
11602       else if (LocaleCompare(value,"png48") == 0)
11603         mng_info->write_png48 = MagickTrue;
11604 
11605       else if (LocaleCompare(value,"png64") == 0)
11606         mng_info->write_png64 = MagickTrue;
11607 
11608       else if ((LocaleCompare(value,"png00") == 0) ||
11609          LocaleCompare(image_info->magick,"PNG00") == 0)
11610         {
11611           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11612           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11613 
11614           if (value != (char *) NULL)
11615             {
11616               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11617                  "  png00 inherited bit depth=%s",value);
11618 
11619               if (LocaleCompare(value,"1") == 0)
11620                 mng_info->write_png_depth = 1;
11621 
11622               else if (LocaleCompare(value,"2") == 0)
11623                 mng_info->write_png_depth = 2;
11624 
11625               else if (LocaleCompare(value,"4") == 0)
11626                 mng_info->write_png_depth = 4;
11627 
11628               else if (LocaleCompare(value,"8") == 0)
11629                 mng_info->write_png_depth = 8;
11630 
11631               else if (LocaleCompare(value,"16") == 0)
11632                 mng_info->write_png_depth = 16;
11633             }
11634 
11635           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11636 
11637           if (value != (char *) NULL)
11638             {
11639               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11640                  "  png00 inherited color type=%s",value);
11641 
11642               if (LocaleCompare(value,"0") == 0)
11643                 mng_info->write_png_colortype = 1;
11644 
11645               else if (LocaleCompare(value,"2") == 0)
11646                 mng_info->write_png_colortype = 3;
11647 
11648               else if (LocaleCompare(value,"3") == 0)
11649                 mng_info->write_png_colortype = 4;
11650 
11651               else if (LocaleCompare(value,"4") == 0)
11652                 mng_info->write_png_colortype = 5;
11653 
11654               else if (LocaleCompare(value,"6") == 0)
11655                 mng_info->write_png_colortype = 7;
11656             }
11657         }
11658     }
11659 
11660   if (mng_info->write_png8)
11661     {
11662       mng_info->write_png_colortype = /* 3 */ 4;
11663       mng_info->write_png_depth = 8;
11664       image->depth = 8;
11665     }
11666 
11667   if (mng_info->write_png24)
11668     {
11669       mng_info->write_png_colortype = /* 2 */ 3;
11670       mng_info->write_png_depth = 8;
11671       image->depth = 8;
11672 
11673       if (image->alpha_trait != UndefinedPixelTrait)
11674         (void) SetImageType(image,TrueColorAlphaType,exception);
11675 
11676       else
11677         (void) SetImageType(image,TrueColorType,exception);
11678 
11679       (void) SyncImage(image,exception);
11680     }
11681 
11682   if (mng_info->write_png32)
11683     {
11684       mng_info->write_png_colortype = /* 6 */  7;
11685       mng_info->write_png_depth = 8;
11686       image->depth = 8;
11687       image->alpha_trait = BlendPixelTrait;
11688 
11689       (void) SetImageType(image,TrueColorAlphaType,exception);
11690       (void) SyncImage(image,exception);
11691     }
11692 
11693   if (mng_info->write_png48)
11694     {
11695       mng_info->write_png_colortype = /* 2 */ 3;
11696       mng_info->write_png_depth = 16;
11697       image->depth = 16;
11698 
11699       if (image->alpha_trait != UndefinedPixelTrait)
11700         (void) SetImageType(image,TrueColorAlphaType,exception);
11701 
11702       else
11703         (void) SetImageType(image,TrueColorType,exception);
11704 
11705       (void) SyncImage(image,exception);
11706     }
11707 
11708   if (mng_info->write_png64)
11709     {
11710       mng_info->write_png_colortype = /* 6 */  7;
11711       mng_info->write_png_depth = 16;
11712       image->depth = 16;
11713       image->alpha_trait = BlendPixelTrait;
11714 
11715       (void) SetImageType(image,TrueColorAlphaType,exception);
11716       (void) SyncImage(image,exception);
11717     }
11718 
11719   value=GetImageOption(image_info,"png:bit-depth");
11720 
11721   if (value != (char *) NULL)
11722     {
11723       if (LocaleCompare(value,"1") == 0)
11724         mng_info->write_png_depth = 1;
11725 
11726       else if (LocaleCompare(value,"2") == 0)
11727         mng_info->write_png_depth = 2;
11728 
11729       else if (LocaleCompare(value,"4") == 0)
11730         mng_info->write_png_depth = 4;
11731 
11732       else if (LocaleCompare(value,"8") == 0)
11733         mng_info->write_png_depth = 8;
11734 
11735       else if (LocaleCompare(value,"16") == 0)
11736         mng_info->write_png_depth = 16;
11737 
11738       else
11739         (void) ThrowMagickException(exception,
11740              GetMagickModule(),CoderWarning,
11741              "ignoring invalid defined png:bit-depth",
11742              "=%s",value);
11743 
11744       if (logging != MagickFalse)
11745         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11746           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11747     }
11748 
11749   value=GetImageOption(image_info,"png:color-type");
11750 
11751   if (value != (char *) NULL)
11752     {
11753       /* We must store colortype+1 because 0 is a valid colortype */
11754       if (LocaleCompare(value,"0") == 0)
11755         mng_info->write_png_colortype = 1;
11756 
11757       else if (LocaleCompare(value,"1") == 0)
11758         mng_info->write_png_colortype = 2;
11759 
11760       else if (LocaleCompare(value,"2") == 0)
11761         mng_info->write_png_colortype = 3;
11762 
11763       else if (LocaleCompare(value,"3") == 0)
11764         mng_info->write_png_colortype = 4;
11765 
11766       else if (LocaleCompare(value,"4") == 0)
11767         mng_info->write_png_colortype = 5;
11768 
11769       else if (LocaleCompare(value,"6") == 0)
11770         mng_info->write_png_colortype = 7;
11771 
11772       else
11773         (void) ThrowMagickException(exception,
11774              GetMagickModule(),CoderWarning,
11775              "ignoring invalid defined png:color-type",
11776              "=%s",value);
11777 
11778       if (logging != MagickFalse)
11779         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11780           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11781     }
11782 
11783   /* Check for chunks to be excluded:
11784    *
11785    * The default is to not exclude any known chunks except for any
11786    * listed in the "unused_chunks" array, above.
11787    *
11788    * Chunks can be listed for exclusion via a "png:exclude-chunk"
11789    * define (in the image properties or in the image artifacts)
11790    * or via a mng_info member.  For convenience, in addition
11791    * to or instead of a comma-separated list of chunks, the
11792    * "exclude-chunk" string can be simply "all" or "none".
11793    *
11794    * The exclude-chunk define takes priority over the mng_info.
11795    *
11796    * A "png:include-chunk" define takes  priority over both the
11797    * mng_info and the "png:exclude-chunk" define.  Like the
11798    * "exclude-chunk" string, it can define "all" or "none" as
11799    * well as a comma-separated list.  Chunks that are unknown to
11800    * ImageMagick are always excluded, regardless of their "copy-safe"
11801    * status according to the PNG specification, and even if they
11802    * appear in the "include-chunk" list. Such defines appearing among
11803    * the image options take priority over those found among the image
11804    * artifacts.
11805    *
11806    * Finally, all chunks listed in the "unused_chunks" array are
11807    * automatically excluded, regardless of the other instructions
11808    * or lack thereof.
11809    *
11810    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11811    * will not be written and the gAMA chunk will only be written if it
11812    * is not between .45 and .46, or approximately (1.0/2.2).
11813    *
11814    * If you exclude tRNS and the image has transparency, the colortype
11815    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11816    *
11817    * The -strip option causes StripImage() to set the png:include-chunk
11818    * artifact to "none,trns,gama".
11819    */
11820 
11821   mng_info->ping_exclude_bKGD=MagickFalse;
11822   mng_info->ping_exclude_cHRM=MagickFalse;
11823   mng_info->ping_exclude_date=MagickFalse;
11824   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11825   mng_info->ping_exclude_gAMA=MagickFalse;
11826   mng_info->ping_exclude_iCCP=MagickFalse;
11827   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11828   mng_info->ping_exclude_oFFs=MagickFalse;
11829   mng_info->ping_exclude_pHYs=MagickFalse;
11830   mng_info->ping_exclude_sRGB=MagickFalse;
11831   mng_info->ping_exclude_tEXt=MagickFalse;
11832   mng_info->ping_exclude_tIME=MagickFalse;
11833   mng_info->ping_exclude_tRNS=MagickFalse;
11834   mng_info->ping_exclude_vpAg=MagickFalse;
11835   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11836   mng_info->ping_exclude_zTXt=MagickFalse;
11837 
11838   mng_info->ping_preserve_colormap=MagickFalse;
11839 
11840   value=GetImageOption(image_info,"png:preserve-colormap");
11841   if (value == NULL)
11842      value=GetImageArtifact(image,"png:preserve-colormap");
11843   if (value != NULL)
11844      mng_info->ping_preserve_colormap=MagickTrue;
11845 
11846   mng_info->ping_preserve_iCCP=MagickFalse;
11847 
11848   value=GetImageOption(image_info,"png:preserve-iCCP");
11849   if (value == NULL)
11850      value=GetImageArtifact(image,"png:preserve-iCCP");
11851   if (value != NULL)
11852      mng_info->ping_preserve_iCCP=MagickTrue;
11853 
11854   /* These compression-level, compression-strategy, and compression-filter
11855    * defines take precedence over values from the -quality option.
11856    */
11857   value=GetImageOption(image_info,"png:compression-level");
11858   if (value == NULL)
11859      value=GetImageArtifact(image,"png:compression-level");
11860   if (value != NULL)
11861   {
11862       /* We have to add 1 to everything because 0 is a valid input,
11863        * and we want to use 0 (the default) to mean undefined.
11864        */
11865       if (LocaleCompare(value,"0") == 0)
11866         mng_info->write_png_compression_level = 1;
11867 
11868       else if (LocaleCompare(value,"1") == 0)
11869         mng_info->write_png_compression_level = 2;
11870 
11871       else if (LocaleCompare(value,"2") == 0)
11872         mng_info->write_png_compression_level = 3;
11873 
11874       else if (LocaleCompare(value,"3") == 0)
11875         mng_info->write_png_compression_level = 4;
11876 
11877       else if (LocaleCompare(value,"4") == 0)
11878         mng_info->write_png_compression_level = 5;
11879 
11880       else if (LocaleCompare(value,"5") == 0)
11881         mng_info->write_png_compression_level = 6;
11882 
11883       else if (LocaleCompare(value,"6") == 0)
11884         mng_info->write_png_compression_level = 7;
11885 
11886       else if (LocaleCompare(value,"7") == 0)
11887         mng_info->write_png_compression_level = 8;
11888 
11889       else if (LocaleCompare(value,"8") == 0)
11890         mng_info->write_png_compression_level = 9;
11891 
11892       else if (LocaleCompare(value,"9") == 0)
11893         mng_info->write_png_compression_level = 10;
11894 
11895       else
11896         (void) ThrowMagickException(exception,
11897              GetMagickModule(),CoderWarning,
11898              "ignoring invalid defined png:compression-level",
11899              "=%s",value);
11900     }
11901 
11902   value=GetImageOption(image_info,"png:compression-strategy");
11903   if (value == NULL)
11904      value=GetImageArtifact(image,"png:compression-strategy");
11905   if (value != NULL)
11906   {
11907       if (LocaleCompare(value,"0") == 0)
11908         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11909 
11910       else if (LocaleCompare(value,"1") == 0)
11911         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11912 
11913       else if (LocaleCompare(value,"2") == 0)
11914         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11915 
11916       else if (LocaleCompare(value,"3") == 0)
11917 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11918         mng_info->write_png_compression_strategy = Z_RLE+1;
11919 #else
11920         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11921 #endif
11922 
11923       else if (LocaleCompare(value,"4") == 0)
11924 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11925         mng_info->write_png_compression_strategy = Z_FIXED+1;
11926 #else
11927         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11928 #endif
11929 
11930       else
11931         (void) ThrowMagickException(exception,
11932              GetMagickModule(),CoderWarning,
11933              "ignoring invalid defined png:compression-strategy",
11934              "=%s",value);
11935     }
11936 
11937   value=GetImageOption(image_info,"png:compression-filter");
11938   if (value == NULL)
11939      value=GetImageArtifact(image,"png:compression-filter");
11940   if (value != NULL)
11941   {
11942       /* To do: combinations of filters allowed by libpng
11943        * masks 0x08 through 0xf8
11944        *
11945        * Implement this as a comma-separated list of 0,1,2,3,4,5
11946        * where 5 is a special case meaning PNG_ALL_FILTERS.
11947        */
11948 
11949       if (LocaleCompare(value,"0") == 0)
11950         mng_info->write_png_compression_filter = 1;
11951 
11952       else if (LocaleCompare(value,"1") == 0)
11953         mng_info->write_png_compression_filter = 2;
11954 
11955       else if (LocaleCompare(value,"2") == 0)
11956         mng_info->write_png_compression_filter = 3;
11957 
11958       else if (LocaleCompare(value,"3") == 0)
11959         mng_info->write_png_compression_filter = 4;
11960 
11961       else if (LocaleCompare(value,"4") == 0)
11962         mng_info->write_png_compression_filter = 5;
11963 
11964       else if (LocaleCompare(value,"5") == 0)
11965         mng_info->write_png_compression_filter = 6;
11966 
11967       else
11968         (void) ThrowMagickException(exception,
11969              GetMagickModule(),CoderWarning,
11970              "ignoring invalid defined png:compression-filter",
11971              "=%s",value);
11972   }
11973 
11974   for (source=0; source<8; source++)
11975   {
11976     value = NULL;
11977 
11978     if (source == 0)
11979       value=GetImageOption(image_info,"png:exclude-chunks");
11980 
11981     if (source == 1)
11982       value=GetImageArtifact(image,"png:exclude-chunks");
11983 
11984     if (source == 2)
11985       value=GetImageOption(image_info,"png:exclude-chunk");
11986 
11987     if (source == 3)
11988       value=GetImageArtifact(image,"png:exclude-chunk");
11989 
11990     if (source == 4)
11991       value=GetImageOption(image_info,"png:include-chunks");
11992 
11993     if (source == 5)
11994       value=GetImageArtifact(image,"png:include-chunks");
11995 
11996     if (source == 6)
11997       value=GetImageOption(image_info,"png:include-chunk");
11998 
11999     if (source == 7)
12000       value=GetImageArtifact(image,"png:include-chunk");
12001 
12002     if (value == NULL)
12003        continue;
12004 
12005     if (source < 4)
12006       excluding = MagickTrue;
12007     else
12008       excluding = MagickFalse;
12009 
12010     if (logging != MagickFalse)
12011       {
12012         if (source == 0 || source == 2)
12013            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12014               "  png:exclude-chunk=%s found in image options.\n", value);
12015         else if (source == 1 || source == 3)
12016            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12017               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12018         else if (source == 4 || source == 6)
12019            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12020               "  png:include-chunk=%s found in image options.\n", value);
12021         else /* if (source == 5 || source == 7) */
12022            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12023               "  png:include-chunk=%s found in image artifacts.\n", value);
12024       }
12025 
12026     if (IsOptionMember("all",value) != MagickFalse)
12027       {
12028         mng_info->ping_exclude_bKGD=excluding;
12029         mng_info->ping_exclude_cHRM=excluding;
12030         mng_info->ping_exclude_date=excluding;
12031         mng_info->ping_exclude_EXIF=excluding;
12032         mng_info->ping_exclude_gAMA=excluding;
12033         mng_info->ping_exclude_iCCP=excluding;
12034         /* mng_info->ping_exclude_iTXt=excluding; */
12035         mng_info->ping_exclude_oFFs=excluding;
12036         mng_info->ping_exclude_pHYs=excluding;
12037         mng_info->ping_exclude_sRGB=excluding;
12038         mng_info->ping_exclude_tEXt=excluding;
12039         mng_info->ping_exclude_tIME=excluding;
12040         mng_info->ping_exclude_tRNS=excluding;
12041         mng_info->ping_exclude_vpAg=excluding;
12042         mng_info->ping_exclude_zCCP=excluding;
12043         mng_info->ping_exclude_zTXt=excluding;
12044       }
12045 
12046     if (IsOptionMember("none",value) != MagickFalse)
12047       {
12048         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12049           MagickTrue;
12050         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12051           MagickTrue;
12052         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12053           MagickTrue;
12054         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12055           MagickTrue;
12056         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12057           MagickTrue;
12058         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12059           MagickTrue;
12060         /* mng_info->ping_exclude_iTXt=!excluding; */
12061         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12062           MagickTrue;
12063         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12064           MagickTrue;
12065         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12066           MagickTrue;
12067         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12068           MagickTrue;
12069         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12070           MagickTrue;
12071         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12072           MagickTrue;
12073         mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12074           MagickTrue;
12075         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12076           MagickTrue;
12077         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12078           MagickTrue;
12079       }
12080 
12081     if (IsOptionMember("bkgd",value) != MagickFalse)
12082       mng_info->ping_exclude_bKGD=excluding;
12083 
12084     if (IsOptionMember("chrm",value) != MagickFalse)
12085       mng_info->ping_exclude_cHRM=excluding;
12086 
12087     if (IsOptionMember("date",value) != MagickFalse)
12088       mng_info->ping_exclude_date=excluding;
12089 
12090     if (IsOptionMember("exif",value) != MagickFalse)
12091       mng_info->ping_exclude_EXIF=excluding;
12092 
12093     if (IsOptionMember("gama",value) != MagickFalse)
12094       mng_info->ping_exclude_gAMA=excluding;
12095 
12096     if (IsOptionMember("iccp",value) != MagickFalse)
12097       mng_info->ping_exclude_iCCP=excluding;
12098 
12099 #if 0
12100     if (IsOptionMember("itxt",value) != MagickFalse)
12101       mng_info->ping_exclude_iTXt=excluding;
12102 #endif
12103 
12104     if (IsOptionMember("offs",value) != MagickFalse)
12105       mng_info->ping_exclude_oFFs=excluding;
12106 
12107     if (IsOptionMember("phys",value) != MagickFalse)
12108       mng_info->ping_exclude_pHYs=excluding;
12109 
12110     if (IsOptionMember("srgb",value) != MagickFalse)
12111       mng_info->ping_exclude_sRGB=excluding;
12112 
12113     if (IsOptionMember("text",value) != MagickFalse)
12114       mng_info->ping_exclude_tEXt=excluding;
12115 
12116     if (IsOptionMember("time",value) != MagickFalse)
12117       mng_info->ping_exclude_tIME=excluding;
12118 
12119     if (IsOptionMember("trns",value) != MagickFalse)
12120       mng_info->ping_exclude_tRNS=excluding;
12121 
12122     if (IsOptionMember("vpag",value) != MagickFalse)
12123       mng_info->ping_exclude_vpAg=excluding;
12124 
12125     if (IsOptionMember("zccp",value) != MagickFalse)
12126       mng_info->ping_exclude_zCCP=excluding;
12127 
12128     if (IsOptionMember("ztxt",value) != MagickFalse)
12129       mng_info->ping_exclude_zTXt=excluding;
12130   }
12131 
12132   if (logging != MagickFalse)
12133   {
12134     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12135       "  Chunks to be excluded from the output png:");
12136     if (mng_info->ping_exclude_bKGD != MagickFalse)
12137       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12138           "    bKGD");
12139     if (mng_info->ping_exclude_cHRM != MagickFalse)
12140       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12141           "    cHRM");
12142     if (mng_info->ping_exclude_date != MagickFalse)
12143       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12144           "    date");
12145     if (mng_info->ping_exclude_EXIF != MagickFalse)
12146       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12147           "    EXIF");
12148     if (mng_info->ping_exclude_gAMA != MagickFalse)
12149       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12150           "    gAMA");
12151     if (mng_info->ping_exclude_iCCP != MagickFalse)
12152       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12153           "    iCCP");
12154 #if 0
12155     if (mng_info->ping_exclude_iTXt != MagickFalse)
12156       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12157           "    iTXt");
12158 #endif
12159 
12160     if (mng_info->ping_exclude_oFFs != MagickFalse)
12161       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12162           "    oFFs");
12163     if (mng_info->ping_exclude_pHYs != MagickFalse)
12164       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12165           "    pHYs");
12166     if (mng_info->ping_exclude_sRGB != MagickFalse)
12167       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12168           "    sRGB");
12169     if (mng_info->ping_exclude_tEXt != MagickFalse)
12170       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12171           "    tEXt");
12172     if (mng_info->ping_exclude_tIME != MagickFalse)
12173       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12174           "    tIME");
12175     if (mng_info->ping_exclude_tRNS != MagickFalse)
12176       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12177           "    tRNS");
12178     if (mng_info->ping_exclude_vpAg != MagickFalse)
12179       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12180           "    vpAg");
12181     if (mng_info->ping_exclude_zCCP != MagickFalse)
12182       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12183           "    zCCP");
12184     if (mng_info->ping_exclude_zTXt != MagickFalse)
12185       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12186           "    zTXt");
12187   }
12188 
12189   mng_info->need_blob = MagickTrue;
12190 
12191   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12192 
12193   MngInfoFreeStruct(mng_info,&have_mng_structure);
12194 
12195   if (logging != MagickFalse)
12196     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12197 
12198   return(status);
12199 }
12200 
12201 #if defined(JNG_SUPPORTED)
12202 
12203 /* Write one JNG image */
WriteOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12204 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12205    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12206 {
12207   Image
12208     *jpeg_image;
12209 
12210   ImageInfo
12211     *jpeg_image_info;
12212 
12213   MagickBooleanType
12214     logging,
12215     status;
12216 
12217   size_t
12218     length;
12219 
12220   unsigned char
12221     *blob,
12222     chunk[80],
12223     *p;
12224 
12225   unsigned int
12226     jng_alpha_compression_method,
12227     jng_alpha_sample_depth,
12228     jng_color_type,
12229     transparent;
12230 
12231   size_t
12232     jng_alpha_quality,
12233     jng_quality;
12234 
12235   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12236     "  Enter WriteOneJNGImage()");
12237 
12238   blob=(unsigned char *) NULL;
12239   jpeg_image=(Image *) NULL;
12240   jpeg_image_info=(ImageInfo *) NULL;
12241   length=0;
12242 
12243   status=MagickTrue;
12244   transparent=image_info->type==GrayscaleAlphaType ||
12245      image_info->type==TrueColorAlphaType ||
12246      image->alpha_trait != UndefinedPixelTrait;
12247 
12248   jng_alpha_sample_depth = 0;
12249 
12250   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12251 
12252   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12253 
12254   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12255       image_info->quality;
12256 
12257   if (jng_alpha_quality >= 1000)
12258     jng_alpha_quality /= 1000;
12259 
12260   length=0;
12261 
12262   if (transparent != 0)
12263     {
12264       jng_color_type=14;
12265 
12266       /* Create JPEG blob, image, and image_info */
12267       if (logging != MagickFalse)
12268         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12269           "  Creating jpeg_image_info for alpha.");
12270 
12271       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12272 
12273       if (jpeg_image_info == (ImageInfo *) NULL)
12274         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12275 
12276       if (logging != MagickFalse)
12277         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12278           "  Creating jpeg_image.");
12279 
12280       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12281       if (jpeg_image == (Image *) NULL)
12282         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12283       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12284       jpeg_image->alpha_trait=UndefinedPixelTrait;
12285       jpeg_image->quality=jng_alpha_quality;
12286       jpeg_image_info->type=GrayscaleType;
12287       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12288       (void) AcquireUniqueFilename(jpeg_image->filename);
12289       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12290         "%s",jpeg_image->filename);
12291     }
12292   else
12293     {
12294       jng_alpha_compression_method=0;
12295       jng_color_type=10;
12296       jng_alpha_sample_depth=0;
12297     }
12298 
12299   /* To do: check bit depth of PNG alpha channel */
12300 
12301   /* Check if image is grayscale. */
12302   if (image_info->type != TrueColorAlphaType && image_info->type !=
12303     TrueColorType && SetImageGray(image,exception))
12304     jng_color_type-=2;
12305 
12306   if (logging != MagickFalse)
12307     {
12308         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12309           "    JNG Quality           = %d",(int) jng_quality);
12310         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12311           "    JNG Color Type        = %d",jng_color_type);
12312         if (transparent != 0)
12313           {
12314             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12315               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12316             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12317               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12318             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12319               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12320           }
12321     }
12322 
12323   if (transparent != 0)
12324     {
12325       if (jng_alpha_compression_method==0)
12326         {
12327           const char
12328             *value;
12329 
12330           /* Encode alpha as a grayscale PNG blob */
12331           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12332             exception);
12333           if (status == MagickFalse)
12334             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12335 
12336           if (logging != MagickFalse)
12337             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12338               "  Creating PNG blob.");
12339 
12340           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MagickPathExtent);
12341           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12342           jpeg_image_info->interlace=NoInterlace;
12343 
12344           /* Exclude all ancillary chunks */
12345           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12346 
12347           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12348             &length,exception);
12349 
12350           /* Retrieve sample depth used */
12351           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12352           if (value != (char *) NULL)
12353             jng_alpha_sample_depth= (unsigned int) value[0];
12354         }
12355       else
12356         {
12357           /* Encode alpha as a grayscale JPEG blob */
12358 
12359           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12360             exception);
12361           if (status == MagickFalse)
12362             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12363 
12364 
12365           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12366           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12367           jpeg_image_info->interlace=NoInterlace;
12368           if (logging != MagickFalse)
12369             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12370               "  Creating blob.");
12371           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12372            exception);
12373           jng_alpha_sample_depth=8;
12374 
12375           if (logging != MagickFalse)
12376             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12377               "  Successfully read jpeg_image into a blob, length=%.20g.",
12378               (double) length);
12379 
12380         }
12381       /* Destroy JPEG image and image_info */
12382       jpeg_image=DestroyImage(jpeg_image);
12383       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12384       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12385     }
12386 
12387   /* Write JHDR chunk */
12388   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12389   PNGType(chunk,mng_JHDR);
12390   LogPNGChunk(logging,mng_JHDR,16L);
12391   PNGLong(chunk+4,(png_uint_32) image->columns);
12392   PNGLong(chunk+8,(png_uint_32) image->rows);
12393   chunk[12]=jng_color_type;
12394   chunk[13]=8;  /* sample depth */
12395   chunk[14]=8; /*jng_image_compression_method */
12396   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12397   chunk[16]=jng_alpha_sample_depth;
12398   chunk[17]=jng_alpha_compression_method;
12399   chunk[18]=0; /*jng_alpha_filter_method */
12400   chunk[19]=0; /*jng_alpha_interlace_method */
12401   (void) WriteBlob(image,20,chunk);
12402   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12403   if (logging != MagickFalse)
12404     {
12405       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12406         "    JNG width:%15lu",(unsigned long) image->columns);
12407 
12408       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12409         "    JNG height:%14lu",(unsigned long) image->rows);
12410 
12411       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12412         "    JNG color type:%10d",jng_color_type);
12413 
12414       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12415         "    JNG sample depth:%8d",8);
12416 
12417       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12418         "    JNG compression:%9d",8);
12419 
12420       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12421         "    JNG interlace:%11d",0);
12422 
12423       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12424         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12425 
12426       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12427         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12428 
12429       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12430         "    JNG alpha filter:%8d",0);
12431 
12432       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12433         "    JNG alpha interlace:%5d",0);
12434     }
12435 
12436   /* Write any JNG-chunk-b profiles */
12437   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12438 
12439   /*
12440      Write leading ancillary chunks
12441   */
12442 
12443   if (transparent != 0)
12444   {
12445     /*
12446       Write JNG bKGD chunk
12447     */
12448 
12449     unsigned char
12450       blue,
12451       green,
12452       red;
12453 
12454     ssize_t
12455       num_bytes;
12456 
12457     if (jng_color_type == 8 || jng_color_type == 12)
12458       num_bytes=6L;
12459     else
12460       num_bytes=10L;
12461     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12462     PNGType(chunk,mng_bKGD);
12463     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12464     red=ScaleQuantumToChar(image->background_color.red);
12465     green=ScaleQuantumToChar(image->background_color.green);
12466     blue=ScaleQuantumToChar(image->background_color.blue);
12467     *(chunk+4)=0;
12468     *(chunk+5)=red;
12469     *(chunk+6)=0;
12470     *(chunk+7)=green;
12471     *(chunk+8)=0;
12472     *(chunk+9)=blue;
12473     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12474     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12475   }
12476 
12477   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12478     {
12479       /*
12480         Write JNG sRGB chunk
12481       */
12482       (void) WriteBlobMSBULong(image,1L);
12483       PNGType(chunk,mng_sRGB);
12484       LogPNGChunk(logging,mng_sRGB,1L);
12485 
12486       if (image->rendering_intent != UndefinedIntent)
12487         chunk[4]=(unsigned char)
12488           Magick_RenderingIntent_to_PNG_RenderingIntent(
12489           (image->rendering_intent));
12490 
12491       else
12492         chunk[4]=(unsigned char)
12493           Magick_RenderingIntent_to_PNG_RenderingIntent(
12494           (PerceptualIntent));
12495 
12496       (void) WriteBlob(image,5,chunk);
12497       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12498     }
12499   else
12500     {
12501       if (image->gamma != 0.0)
12502         {
12503           /*
12504              Write JNG gAMA chunk
12505           */
12506           (void) WriteBlobMSBULong(image,4L);
12507           PNGType(chunk,mng_gAMA);
12508           LogPNGChunk(logging,mng_gAMA,4L);
12509           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12510           (void) WriteBlob(image,8,chunk);
12511           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12512         }
12513 
12514       if ((mng_info->equal_chrms == MagickFalse) &&
12515           (image->chromaticity.red_primary.x != 0.0))
12516         {
12517           PrimaryInfo
12518             primary;
12519 
12520           /*
12521              Write JNG cHRM chunk
12522           */
12523           (void) WriteBlobMSBULong(image,32L);
12524           PNGType(chunk,mng_cHRM);
12525           LogPNGChunk(logging,mng_cHRM,32L);
12526           primary=image->chromaticity.white_point;
12527           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12528           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12529           primary=image->chromaticity.red_primary;
12530           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12531           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12532           primary=image->chromaticity.green_primary;
12533           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12534           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12535           primary=image->chromaticity.blue_primary;
12536           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12537           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12538           (void) WriteBlob(image,36,chunk);
12539           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12540         }
12541     }
12542 
12543   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12544     {
12545       /*
12546          Write JNG pHYs chunk
12547       */
12548       (void) WriteBlobMSBULong(image,9L);
12549       PNGType(chunk,mng_pHYs);
12550       LogPNGChunk(logging,mng_pHYs,9L);
12551       if (image->units == PixelsPerInchResolution)
12552         {
12553           PNGLong(chunk+4,(png_uint_32)
12554             (image->resolution.x*100.0/2.54+0.5));
12555 
12556           PNGLong(chunk+8,(png_uint_32)
12557             (image->resolution.y*100.0/2.54+0.5));
12558 
12559           chunk[12]=1;
12560         }
12561 
12562       else
12563         {
12564           if (image->units == PixelsPerCentimeterResolution)
12565             {
12566               PNGLong(chunk+4,(png_uint_32)
12567                 (image->resolution.x*100.0+0.5));
12568 
12569               PNGLong(chunk+8,(png_uint_32)
12570                 (image->resolution.y*100.0+0.5));
12571 
12572               chunk[12]=1;
12573             }
12574 
12575           else
12576             {
12577               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12578               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12579               chunk[12]=0;
12580             }
12581         }
12582       (void) WriteBlob(image,13,chunk);
12583       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12584     }
12585 
12586   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12587     {
12588       /*
12589          Write JNG oFFs chunk
12590       */
12591       (void) WriteBlobMSBULong(image,9L);
12592       PNGType(chunk,mng_oFFs);
12593       LogPNGChunk(logging,mng_oFFs,9L);
12594       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12595       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12596       chunk[12]=0;
12597       (void) WriteBlob(image,13,chunk);
12598       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12599     }
12600   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12601     {
12602        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12603        PNGType(chunk,mng_vpAg);
12604        LogPNGChunk(logging,mng_vpAg,9L);
12605        PNGLong(chunk+4,(png_uint_32) image->page.width);
12606        PNGLong(chunk+8,(png_uint_32) image->page.height);
12607        chunk[12]=0;   /* unit = pixels */
12608        (void) WriteBlob(image,13,chunk);
12609        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12610     }
12611 
12612   if (transparent != 0)
12613     {
12614       if (jng_alpha_compression_method==0)
12615         {
12616           register ssize_t
12617             i;
12618 
12619           size_t
12620             len;
12621 
12622           /* Write IDAT chunk header */
12623           if (logging != MagickFalse)
12624             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12625               "  Write IDAT chunks from blob, length=%.20g.",(double)
12626               length);
12627 
12628           /* Copy IDAT chunks */
12629           len=0;
12630           p=blob+8;
12631           for (i=8; i<(ssize_t) length; i+=len+12)
12632           {
12633             len=(size_t) (*p) << 24;
12634             len|=(size_t) (*(p+1)) << 16;
12635             len|=(size_t) (*(p+2)) << 8;
12636             len|=(size_t) (*(p+3));
12637             p+=4;
12638 
12639             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12640               {
12641                 /* Found an IDAT chunk. */
12642                 (void) WriteBlobMSBULong(image,len);
12643                 LogPNGChunk(logging,mng_IDAT,len);
12644                 (void) WriteBlob(image,len+4,p);
12645                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12646               }
12647 
12648             else
12649               {
12650                 if (logging != MagickFalse)
12651                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12652                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12653                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12654               }
12655             p+=(8+len);
12656           }
12657         }
12658       else if (length != 0)
12659         {
12660           /* Write JDAA chunk header */
12661           if (logging != MagickFalse)
12662             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12663               "  Write JDAA chunk, length=%.20g.",(double) length);
12664           (void) WriteBlobMSBULong(image,(size_t) length);
12665           PNGType(chunk,mng_JDAA);
12666           LogPNGChunk(logging,mng_JDAA,length);
12667           /* Write JDAT chunk(s) data */
12668           (void) WriteBlob(image,4,chunk);
12669           (void) WriteBlob(image,length,blob);
12670           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12671              (uInt) length));
12672         }
12673       blob=(unsigned char *) RelinquishMagickMemory(blob);
12674     }
12675 
12676   /* Encode image as a JPEG blob */
12677   if (logging != MagickFalse)
12678     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12679       "  Creating jpeg_image_info.");
12680   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12681   if (jpeg_image_info == (ImageInfo *) NULL)
12682     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12683 
12684   if (logging != MagickFalse)
12685     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12686       "  Creating jpeg_image.");
12687 
12688   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12689   if (jpeg_image == (Image *) NULL)
12690     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12691   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12692 
12693   (void) AcquireUniqueFilename(jpeg_image->filename);
12694   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
12695     jpeg_image->filename);
12696 
12697   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12698     exception);
12699 
12700   if (logging != MagickFalse)
12701     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12702       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12703       (double) jpeg_image->rows);
12704 
12705   if (status == MagickFalse)
12706     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12707 
12708   if (jng_color_type == 8 || jng_color_type == 12)
12709     jpeg_image_info->type=GrayscaleType;
12710 
12711   jpeg_image_info->quality=jng_quality;
12712   jpeg_image->quality=jng_quality;
12713   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12714   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12715 
12716   if (logging != MagickFalse)
12717     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12718       "  Creating blob.");
12719 
12720   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12721     exception);
12722 
12723   if (logging != MagickFalse)
12724     {
12725       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12726         "  Successfully read jpeg_image into a blob, length=%.20g.",
12727         (double) length);
12728 
12729       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12730         "  Write JDAT chunk, length=%.20g.",(double) length);
12731     }
12732 
12733   /* Write JDAT chunk(s) */
12734   (void) WriteBlobMSBULong(image,(size_t) length);
12735   PNGType(chunk,mng_JDAT);
12736   LogPNGChunk(logging,mng_JDAT,length);
12737   (void) WriteBlob(image,4,chunk);
12738   (void) WriteBlob(image,length,blob);
12739   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12740 
12741   jpeg_image=DestroyImage(jpeg_image);
12742   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12743   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12744   blob=(unsigned char *) RelinquishMagickMemory(blob);
12745 
12746   /* Write any JNG-chunk-e profiles */
12747   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12748 
12749   /* Write IEND chunk */
12750   (void) WriteBlobMSBULong(image,0L);
12751   PNGType(chunk,mng_IEND);
12752   LogPNGChunk(logging,mng_IEND,0);
12753   (void) WriteBlob(image,4,chunk);
12754   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12755 
12756   if (logging != MagickFalse)
12757     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12758       "  exit WriteOneJNGImage()");
12759 
12760   return(status);
12761 }
12762 
12763 
12764 /*
12765 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12766 %                                                                             %
12767 %                                                                             %
12768 %                                                                             %
12769 %   W r i t e J N G I m a g e                                                 %
12770 %                                                                             %
12771 %                                                                             %
12772 %                                                                             %
12773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12774 %
12775 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12776 %
12777 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12778 %
12779 %  The format of the WriteJNGImage method is:
12780 %
12781 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12782 %        Image *image,ExceptionInfo *exception)
12783 %
12784 %  A description of each parameter follows:
12785 %
12786 %    o image_info: the image info.
12787 %
12788 %    o image:  The image.
12789 %
12790 %    o exception: return any errors or warnings in this structure.
12791 %
12792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12793 */
WriteJNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12794 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12795   ExceptionInfo *exception)
12796 {
12797   MagickBooleanType
12798     have_mng_structure,
12799     logging,
12800     status;
12801 
12802   MngInfo
12803     *mng_info;
12804 
12805   /*
12806     Open image file.
12807   */
12808   assert(image_info != (const ImageInfo *) NULL);
12809   assert(image_info->signature == MagickCoreSignature);
12810   assert(image != (Image *) NULL);
12811   assert(image->signature == MagickCoreSignature);
12812   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12813   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12814   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12815   if (status == MagickFalse)
12816     return(status);
12817   if ((image->columns > 65535UL) || (image->rows > 65535UL))
12818     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
12819 
12820   /*
12821     Allocate a MngInfo structure.
12822   */
12823   have_mng_structure=MagickFalse;
12824   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12825   if (mng_info == (MngInfo *) NULL)
12826     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12827   /*
12828     Initialize members of the MngInfo structure.
12829   */
12830   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12831   mng_info->image=image;
12832   have_mng_structure=MagickTrue;
12833 
12834   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12835 
12836   status=WriteOneJNGImage(mng_info,image_info,image,exception);
12837   (void) CloseBlob(image);
12838 
12839   (void) CatchImageException(image);
12840   MngInfoFreeStruct(mng_info,&have_mng_structure);
12841   if (logging != MagickFalse)
12842     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12843   return(status);
12844 }
12845 #endif
12846 
WriteMNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12847 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12848   ExceptionInfo *exception)
12849 {
12850   const char
12851     *option;
12852 
12853   Image
12854     *next_image;
12855 
12856   MagickBooleanType
12857     have_mng_structure,
12858     status;
12859 
12860   volatile MagickBooleanType
12861     logging;
12862 
12863   MngInfo
12864     *mng_info;
12865 
12866   int
12867     image_count,
12868     need_iterations,
12869     need_matte;
12870 
12871   volatile int
12872 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12873     defined(PNG_MNG_FEATURES_SUPPORTED)
12874     need_local_plte,
12875 #endif
12876     all_images_are_gray,
12877     need_defi,
12878     use_global_plte;
12879 
12880   register ssize_t
12881     i;
12882 
12883   unsigned char
12884     chunk[800];
12885 
12886   volatile unsigned int
12887     write_jng,
12888     write_mng;
12889 
12890   volatile size_t
12891     scene;
12892 
12893   size_t
12894     final_delay=0,
12895     initial_delay;
12896 
12897 #if (PNG_LIBPNG_VER < 10200)
12898     if (image_info->verbose)
12899       printf("Your PNG library (libpng-%s) is rather old.\n",
12900          PNG_LIBPNG_VER_STRING);
12901 #endif
12902 
12903   /*
12904     Open image file.
12905   */
12906   assert(image_info != (const ImageInfo *) NULL);
12907   assert(image_info->signature == MagickCoreSignature);
12908   assert(image != (Image *) NULL);
12909   assert(image->signature == MagickCoreSignature);
12910   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12911   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12912   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12913   if (status == MagickFalse)
12914     return(status);
12915 
12916   /*
12917     Allocate a MngInfo structure.
12918   */
12919   have_mng_structure=MagickFalse;
12920   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12921   if (mng_info == (MngInfo *) NULL)
12922     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12923   /*
12924     Initialize members of the MngInfo structure.
12925   */
12926   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12927   mng_info->image=image;
12928   have_mng_structure=MagickTrue;
12929   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12930 
12931   /*
12932    * See if user has requested a specific PNG subformat to be used
12933    * for all of the PNGs in the MNG being written, e.g.,
12934    *
12935    *    convert *.png png8:animation.mng
12936    *
12937    * To do: check -define png:bit_depth and png:color_type as well,
12938    * or perhaps use mng:bit_depth and mng:color_type instead for
12939    * global settings.
12940    */
12941 
12942   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12943   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12944   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12945 
12946   write_jng=MagickFalse;
12947   if (image_info->compression == JPEGCompression)
12948     write_jng=MagickTrue;
12949 
12950   mng_info->adjoin=image_info->adjoin &&
12951     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12952 
12953   if (logging != MagickFalse)
12954     {
12955       /* Log some info about the input */
12956       Image
12957         *p;
12958 
12959       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12960         "  Checking input image(s)\n"
12961         "    Image_info depth: %.20g,    Type: %d",
12962         (double) image_info->depth, image_info->type);
12963 
12964       scene=0;
12965       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12966       {
12967 
12968         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969            "    Scene: %.20g\n,   Image depth: %.20g",
12970            (double) scene++, (double) p->depth);
12971 
12972         if (p->alpha_trait != UndefinedPixelTrait)
12973           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12974             "      Matte: True");
12975 
12976         else
12977           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12978             "      Matte: False");
12979 
12980         if (p->storage_class == PseudoClass)
12981           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12982             "      Storage class: PseudoClass");
12983 
12984         else
12985           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12986             "      Storage class: DirectClass");
12987 
12988         if (p->colors)
12989           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12990             "      Number of colors: %.20g",(double) p->colors);
12991 
12992         else
12993           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12994             "      Number of colors: unspecified");
12995 
12996         if (mng_info->adjoin == MagickFalse)
12997           break;
12998       }
12999     }
13000 
13001   use_global_plte=MagickFalse;
13002   all_images_are_gray=MagickFalse;
13003 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13004   need_local_plte=MagickTrue;
13005 #endif
13006   need_defi=MagickFalse;
13007   need_matte=MagickFalse;
13008   mng_info->framing_mode=1;
13009   mng_info->old_framing_mode=1;
13010 
13011   if (write_mng)
13012       if (image_info->page != (char *) NULL)
13013         {
13014           /*
13015             Determine image bounding box.
13016           */
13017           SetGeometry(image,&mng_info->page);
13018           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13019             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13020         }
13021   if (write_mng)
13022     {
13023       unsigned int
13024         need_geom;
13025 
13026       unsigned short
13027         red,
13028         green,
13029         blue;
13030 
13031       mng_info->page=image->page;
13032       need_geom=MagickTrue;
13033       if (mng_info->page.width || mng_info->page.height)
13034          need_geom=MagickFalse;
13035       /*
13036         Check all the scenes.
13037       */
13038       initial_delay=image->delay;
13039       need_iterations=MagickFalse;
13040       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13041       mng_info->equal_physs=MagickTrue,
13042       mng_info->equal_gammas=MagickTrue;
13043       mng_info->equal_srgbs=MagickTrue;
13044       mng_info->equal_backgrounds=MagickTrue;
13045       image_count=0;
13046 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13047     defined(PNG_MNG_FEATURES_SUPPORTED)
13048       all_images_are_gray=MagickTrue;
13049       mng_info->equal_palettes=MagickFalse;
13050       need_local_plte=MagickFalse;
13051 #endif
13052       for (next_image=image; next_image != (Image *) NULL; )
13053       {
13054         if (need_geom)
13055           {
13056             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13057               mng_info->page.width=next_image->columns+next_image->page.x;
13058 
13059             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13060               mng_info->page.height=next_image->rows+next_image->page.y;
13061           }
13062 
13063         if (next_image->page.x || next_image->page.y)
13064           need_defi=MagickTrue;
13065 
13066         if (next_image->alpha_trait != UndefinedPixelTrait)
13067           need_matte=MagickTrue;
13068 
13069         if ((int) next_image->dispose >= BackgroundDispose)
13070           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13071                next_image->page.x || next_image->page.y ||
13072               ((next_image->columns < mng_info->page.width) &&
13073                (next_image->rows < mng_info->page.height)))
13074             mng_info->need_fram=MagickTrue;
13075 
13076         if (next_image->iterations)
13077           need_iterations=MagickTrue;
13078 
13079         final_delay=next_image->delay;
13080 
13081         if (final_delay != initial_delay || final_delay > 1UL*
13082            next_image->ticks_per_second)
13083           mng_info->need_fram=1;
13084 
13085 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13086     defined(PNG_MNG_FEATURES_SUPPORTED)
13087         /*
13088           check for global palette possibility.
13089         */
13090         if (image->alpha_trait != UndefinedPixelTrait)
13091            need_local_plte=MagickTrue;
13092 
13093         if (need_local_plte == 0)
13094           {
13095             if (SetImageGray(image,exception) == MagickFalse)
13096               all_images_are_gray=MagickFalse;
13097             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13098             if (use_global_plte == 0)
13099               use_global_plte=mng_info->equal_palettes;
13100             need_local_plte=!mng_info->equal_palettes;
13101           }
13102 #endif
13103         if (GetNextImageInList(next_image) != (Image *) NULL)
13104           {
13105             if (next_image->background_color.red !=
13106                 next_image->next->background_color.red ||
13107                 next_image->background_color.green !=
13108                 next_image->next->background_color.green ||
13109                 next_image->background_color.blue !=
13110                 next_image->next->background_color.blue)
13111               mng_info->equal_backgrounds=MagickFalse;
13112 
13113             if (next_image->gamma != next_image->next->gamma)
13114               mng_info->equal_gammas=MagickFalse;
13115 
13116             if (next_image->rendering_intent !=
13117                 next_image->next->rendering_intent)
13118               mng_info->equal_srgbs=MagickFalse;
13119 
13120             if ((next_image->units != next_image->next->units) ||
13121                 (next_image->resolution.x != next_image->next->resolution.x) ||
13122                 (next_image->resolution.y != next_image->next->resolution.y))
13123               mng_info->equal_physs=MagickFalse;
13124 
13125             if (mng_info->equal_chrms)
13126               {
13127                 if (next_image->chromaticity.red_primary.x !=
13128                     next_image->next->chromaticity.red_primary.x ||
13129                     next_image->chromaticity.red_primary.y !=
13130                     next_image->next->chromaticity.red_primary.y ||
13131                     next_image->chromaticity.green_primary.x !=
13132                     next_image->next->chromaticity.green_primary.x ||
13133                     next_image->chromaticity.green_primary.y !=
13134                     next_image->next->chromaticity.green_primary.y ||
13135                     next_image->chromaticity.blue_primary.x !=
13136                     next_image->next->chromaticity.blue_primary.x ||
13137                     next_image->chromaticity.blue_primary.y !=
13138                     next_image->next->chromaticity.blue_primary.y ||
13139                     next_image->chromaticity.white_point.x !=
13140                     next_image->next->chromaticity.white_point.x ||
13141                     next_image->chromaticity.white_point.y !=
13142                     next_image->next->chromaticity.white_point.y)
13143                   mng_info->equal_chrms=MagickFalse;
13144               }
13145           }
13146         image_count++;
13147         next_image=GetNextImageInList(next_image);
13148       }
13149       if (image_count < 2)
13150         {
13151           mng_info->equal_backgrounds=MagickFalse;
13152           mng_info->equal_chrms=MagickFalse;
13153           mng_info->equal_gammas=MagickFalse;
13154           mng_info->equal_srgbs=MagickFalse;
13155           mng_info->equal_physs=MagickFalse;
13156           use_global_plte=MagickFalse;
13157 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13158           need_local_plte=MagickTrue;
13159 #endif
13160           need_iterations=MagickFalse;
13161         }
13162 
13163      if (mng_info->need_fram == MagickFalse)
13164        {
13165          /*
13166            Only certain framing rates 100/n are exactly representable without
13167            the FRAM chunk but we'll allow some slop in VLC files
13168          */
13169          if (final_delay == 0)
13170            {
13171              if (need_iterations != MagickFalse)
13172                {
13173                  /*
13174                    It's probably a GIF with loop; don't run it *too* fast.
13175                  */
13176                  if (mng_info->adjoin)
13177                    {
13178                      final_delay=10;
13179                      (void) ThrowMagickException(exception,GetMagickModule(),
13180                        CoderWarning,
13181                        "input has zero delay between all frames; assuming",
13182                        " 10 cs `%s'","");
13183                    }
13184                }
13185              else
13186                mng_info->ticks_per_second=0;
13187            }
13188          if (final_delay != 0)
13189            mng_info->ticks_per_second=(png_uint_32)
13190               (image->ticks_per_second/final_delay);
13191          if (final_delay > 50)
13192            mng_info->ticks_per_second=2;
13193 
13194          if (final_delay > 75)
13195            mng_info->ticks_per_second=1;
13196 
13197          if (final_delay > 125)
13198            mng_info->need_fram=MagickTrue;
13199 
13200          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13201             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13202             (final_delay != 25) && (final_delay != 50) &&
13203             (final_delay != (size_t) image->ticks_per_second))
13204            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13205        }
13206 
13207      if (mng_info->need_fram != MagickFalse)
13208         mng_info->ticks_per_second=image->ticks_per_second;
13209      /*
13210         If pseudocolor, we should also check to see if all the
13211         palettes are identical and write a global PLTE if they are.
13212         ../glennrp Feb 99.
13213      */
13214      /*
13215         Write the MNG version 1.0 signature and MHDR chunk.
13216      */
13217      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13218      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13219      PNGType(chunk,mng_MHDR);
13220      LogPNGChunk(logging,mng_MHDR,28L);
13221      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13222      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13223      PNGLong(chunk+12,mng_info->ticks_per_second);
13224      PNGLong(chunk+16,0L);  /* layer count=unknown */
13225      PNGLong(chunk+20,0L);  /* frame count=unknown */
13226      PNGLong(chunk+24,0L);  /* play time=unknown   */
13227      if (write_jng)
13228        {
13229          if (need_matte)
13230            {
13231              if (need_defi || mng_info->need_fram || use_global_plte)
13232                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13233 
13234              else
13235                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13236            }
13237 
13238          else
13239            {
13240              if (need_defi || mng_info->need_fram || use_global_plte)
13241                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13242 
13243              else
13244                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13245            }
13246        }
13247 
13248      else
13249        {
13250          if (need_matte)
13251            {
13252              if (need_defi || mng_info->need_fram || use_global_plte)
13253                PNGLong(chunk+28,11L);    /* simplicity=LC */
13254 
13255              else
13256                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13257            }
13258 
13259          else
13260            {
13261              if (need_defi || mng_info->need_fram || use_global_plte)
13262                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13263 
13264              else
13265                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13266            }
13267        }
13268      (void) WriteBlob(image,32,chunk);
13269      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13270      option=GetImageOption(image_info,"mng:need-cacheoff");
13271      if (option != (const char *) NULL)
13272        {
13273          size_t
13274            length;
13275 
13276          /*
13277            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13278          */
13279          PNGType(chunk,mng_nEED);
13280          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13281          (void) WriteBlobMSBULong(image,(size_t) length);
13282          LogPNGChunk(logging,mng_nEED,(size_t) length);
13283          length+=4;
13284          (void) WriteBlob(image,length,chunk);
13285          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13286        }
13287      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13288          (GetNextImageInList(image) != (Image *) NULL) &&
13289          (image->iterations != 1))
13290        {
13291          /*
13292            Write MNG TERM chunk
13293          */
13294          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13295          PNGType(chunk,mng_TERM);
13296          LogPNGChunk(logging,mng_TERM,10L);
13297          chunk[4]=3;  /* repeat animation */
13298          chunk[5]=0;  /* show last frame when done */
13299          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13300             final_delay/MagickMax(image->ticks_per_second,1)));
13301 
13302          if (image->iterations == 0)
13303            PNGLong(chunk+10,PNG_UINT_31_MAX);
13304 
13305          else
13306            PNGLong(chunk+10,(png_uint_32) image->iterations);
13307 
13308          if (logging != MagickFalse)
13309            {
13310              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13311                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13312               final_delay/MagickMax(image->ticks_per_second,1)));
13313 
13314              if (image->iterations == 0)
13315                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13316                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13317 
13318              else
13319                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13320                  "     Image iterations: %.20g",(double) image->iterations);
13321            }
13322          (void) WriteBlob(image,14,chunk);
13323          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13324        }
13325      /*
13326        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13327      */
13328      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13329           mng_info->equal_srgbs)
13330        {
13331          /*
13332            Write MNG sRGB chunk
13333          */
13334          (void) WriteBlobMSBULong(image,1L);
13335          PNGType(chunk,mng_sRGB);
13336          LogPNGChunk(logging,mng_sRGB,1L);
13337 
13338          if (image->rendering_intent != UndefinedIntent)
13339            chunk[4]=(unsigned char)
13340              Magick_RenderingIntent_to_PNG_RenderingIntent(
13341              (image->rendering_intent));
13342 
13343          else
13344            chunk[4]=(unsigned char)
13345              Magick_RenderingIntent_to_PNG_RenderingIntent(
13346                (PerceptualIntent));
13347 
13348          (void) WriteBlob(image,5,chunk);
13349          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13350          mng_info->have_write_global_srgb=MagickTrue;
13351        }
13352 
13353      else
13354        {
13355          if (image->gamma && mng_info->equal_gammas)
13356            {
13357              /*
13358                 Write MNG gAMA chunk
13359              */
13360              (void) WriteBlobMSBULong(image,4L);
13361              PNGType(chunk,mng_gAMA);
13362              LogPNGChunk(logging,mng_gAMA,4L);
13363              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13364              (void) WriteBlob(image,8,chunk);
13365              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13366              mng_info->have_write_global_gama=MagickTrue;
13367            }
13368          if (mng_info->equal_chrms)
13369            {
13370              PrimaryInfo
13371                primary;
13372 
13373              /*
13374                 Write MNG cHRM chunk
13375              */
13376              (void) WriteBlobMSBULong(image,32L);
13377              PNGType(chunk,mng_cHRM);
13378              LogPNGChunk(logging,mng_cHRM,32L);
13379              primary=image->chromaticity.white_point;
13380              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13381              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13382              primary=image->chromaticity.red_primary;
13383              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13384              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13385              primary=image->chromaticity.green_primary;
13386              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13387              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13388              primary=image->chromaticity.blue_primary;
13389              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13390              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13391              (void) WriteBlob(image,36,chunk);
13392              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13393              mng_info->have_write_global_chrm=MagickTrue;
13394            }
13395        }
13396      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13397        {
13398          /*
13399             Write MNG pHYs chunk
13400          */
13401          (void) WriteBlobMSBULong(image,9L);
13402          PNGType(chunk,mng_pHYs);
13403          LogPNGChunk(logging,mng_pHYs,9L);
13404 
13405          if (image->units == PixelsPerInchResolution)
13406            {
13407              PNGLong(chunk+4,(png_uint_32)
13408                (image->resolution.x*100.0/2.54+0.5));
13409 
13410              PNGLong(chunk+8,(png_uint_32)
13411                (image->resolution.y*100.0/2.54+0.5));
13412 
13413              chunk[12]=1;
13414            }
13415 
13416          else
13417            {
13418              if (image->units == PixelsPerCentimeterResolution)
13419                {
13420                  PNGLong(chunk+4,(png_uint_32)
13421                    (image->resolution.x*100.0+0.5));
13422 
13423                  PNGLong(chunk+8,(png_uint_32)
13424                    (image->resolution.y*100.0+0.5));
13425 
13426                  chunk[12]=1;
13427                }
13428 
13429              else
13430                {
13431                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13432                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13433                  chunk[12]=0;
13434                }
13435            }
13436          (void) WriteBlob(image,13,chunk);
13437          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13438        }
13439      /*
13440        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13441        or does not cover the entire frame.
13442      */
13443      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13444          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13445          (image->page.width+image->page.x < mng_info->page.width))
13446          || (image->page.height && (image->page.height+image->page.y
13447          < mng_info->page.height))))
13448        {
13449          (void) WriteBlobMSBULong(image,6L);
13450          PNGType(chunk,mng_BACK);
13451          LogPNGChunk(logging,mng_BACK,6L);
13452          red=ScaleQuantumToShort(image->background_color.red);
13453          green=ScaleQuantumToShort(image->background_color.green);
13454          blue=ScaleQuantumToShort(image->background_color.blue);
13455          PNGShort(chunk+4,red);
13456          PNGShort(chunk+6,green);
13457          PNGShort(chunk+8,blue);
13458          (void) WriteBlob(image,10,chunk);
13459          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13460          if (mng_info->equal_backgrounds)
13461            {
13462              (void) WriteBlobMSBULong(image,6L);
13463              PNGType(chunk,mng_bKGD);
13464              LogPNGChunk(logging,mng_bKGD,6L);
13465              (void) WriteBlob(image,10,chunk);
13466              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13467            }
13468        }
13469 
13470 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13471      if ((need_local_plte == MagickFalse) &&
13472          (image->storage_class == PseudoClass) &&
13473          (all_images_are_gray == MagickFalse))
13474        {
13475          size_t
13476            data_length;
13477 
13478          /*
13479            Write MNG PLTE chunk
13480          */
13481          data_length=3*image->colors;
13482          (void) WriteBlobMSBULong(image,data_length);
13483          PNGType(chunk,mng_PLTE);
13484          LogPNGChunk(logging,mng_PLTE,data_length);
13485 
13486          for (i=0; i < (ssize_t) image->colors; i++)
13487          {
13488            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13489              image->colormap[i].red) & 0xff);
13490            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13491              image->colormap[i].green) & 0xff);
13492            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13493              image->colormap[i].blue) & 0xff);
13494          }
13495 
13496          (void) WriteBlob(image,data_length+4,chunk);
13497          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13498          mng_info->have_write_global_plte=MagickTrue;
13499        }
13500 #endif
13501     }
13502   scene=0;
13503   mng_info->delay=0;
13504 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13505     defined(PNG_MNG_FEATURES_SUPPORTED)
13506   mng_info->equal_palettes=MagickFalse;
13507 #endif
13508   do
13509   {
13510     if (mng_info->adjoin)
13511     {
13512 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13513     defined(PNG_MNG_FEATURES_SUPPORTED)
13514     /*
13515       If we aren't using a global palette for the entire MNG, check to
13516       see if we can use one for two or more consecutive images.
13517     */
13518     if (need_local_plte && use_global_plte && !all_images_are_gray)
13519       {
13520         if (mng_info->IsPalette)
13521           {
13522             /*
13523               When equal_palettes is true, this image has the same palette
13524               as the previous PseudoClass image
13525             */
13526             mng_info->have_write_global_plte=mng_info->equal_palettes;
13527             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13528             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13529               {
13530                 /*
13531                   Write MNG PLTE chunk
13532                 */
13533                 size_t
13534                   data_length;
13535 
13536                 data_length=3*image->colors;
13537                 (void) WriteBlobMSBULong(image,data_length);
13538                 PNGType(chunk,mng_PLTE);
13539                 LogPNGChunk(logging,mng_PLTE,data_length);
13540 
13541                 for (i=0; i < (ssize_t) image->colors; i++)
13542                 {
13543                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13544                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13545                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13546                 }
13547 
13548                 (void) WriteBlob(image,data_length+4,chunk);
13549                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13550                    (uInt) (data_length+4)));
13551                 mng_info->have_write_global_plte=MagickTrue;
13552               }
13553           }
13554         else
13555           mng_info->have_write_global_plte=MagickFalse;
13556       }
13557 #endif
13558     if (need_defi)
13559       {
13560         ssize_t
13561           previous_x,
13562           previous_y;
13563 
13564         if (scene)
13565           {
13566             previous_x=mng_info->page.x;
13567             previous_y=mng_info->page.y;
13568           }
13569         else
13570           {
13571             previous_x=0;
13572             previous_y=0;
13573           }
13574         mng_info->page=image->page;
13575         if ((mng_info->page.x !=  previous_x) ||
13576             (mng_info->page.y != previous_y))
13577           {
13578              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13579              PNGType(chunk,mng_DEFI);
13580              LogPNGChunk(logging,mng_DEFI,12L);
13581              chunk[4]=0; /* object 0 MSB */
13582              chunk[5]=0; /* object 0 LSB */
13583              chunk[6]=0; /* visible  */
13584              chunk[7]=0; /* abstract */
13585              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13586              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13587              (void) WriteBlob(image,16,chunk);
13588              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13589           }
13590       }
13591     }
13592 
13593    mng_info->write_mng=write_mng;
13594 
13595    if ((int) image->dispose >= 3)
13596      mng_info->framing_mode=3;
13597 
13598    if (mng_info->need_fram && mng_info->adjoin &&
13599        ((image->delay != mng_info->delay) ||
13600         (mng_info->framing_mode != mng_info->old_framing_mode)))
13601      {
13602        if (image->delay == mng_info->delay)
13603          {
13604            /*
13605              Write a MNG FRAM chunk with the new framing mode.
13606            */
13607            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13608            PNGType(chunk,mng_FRAM);
13609            LogPNGChunk(logging,mng_FRAM,1L);
13610            chunk[4]=(unsigned char) mng_info->framing_mode;
13611            (void) WriteBlob(image,5,chunk);
13612            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13613          }
13614        else
13615          {
13616            /*
13617              Write a MNG FRAM chunk with the delay.
13618            */
13619            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13620            PNGType(chunk,mng_FRAM);
13621            LogPNGChunk(logging,mng_FRAM,10L);
13622            chunk[4]=(unsigned char) mng_info->framing_mode;
13623            chunk[5]=0;  /* frame name separator (no name) */
13624            chunk[6]=2;  /* flag for changing default delay */
13625            chunk[7]=0;  /* flag for changing frame timeout */
13626            chunk[8]=0;  /* flag for changing frame clipping */
13627            chunk[9]=0;  /* flag for changing frame sync_id */
13628            PNGLong(chunk+10,(png_uint_32)
13629              ((mng_info->ticks_per_second*
13630              image->delay)/MagickMax(image->ticks_per_second,1)));
13631            (void) WriteBlob(image,14,chunk);
13632            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13633            mng_info->delay=(png_uint_32) image->delay;
13634          }
13635        mng_info->old_framing_mode=mng_info->framing_mode;
13636      }
13637 
13638 #if defined(JNG_SUPPORTED)
13639    if (image_info->compression == JPEGCompression)
13640      {
13641        ImageInfo
13642          *write_info;
13643 
13644        if (logging != MagickFalse)
13645          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13646            "  Writing JNG object.");
13647        /* To do: specify the desired alpha compression method. */
13648        write_info=CloneImageInfo(image_info);
13649        write_info->compression=UndefinedCompression;
13650        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13651        write_info=DestroyImageInfo(write_info);
13652      }
13653    else
13654 #endif
13655      {
13656        if (logging != MagickFalse)
13657          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13658            "  Writing PNG object.");
13659 
13660        mng_info->need_blob = MagickFalse;
13661        mng_info->ping_preserve_colormap = MagickFalse;
13662 
13663        /* We don't want any ancillary chunks written */
13664        mng_info->ping_exclude_bKGD=MagickTrue;
13665        mng_info->ping_exclude_cHRM=MagickTrue;
13666        mng_info->ping_exclude_date=MagickTrue;
13667        mng_info->ping_exclude_EXIF=MagickTrue;
13668        mng_info->ping_exclude_gAMA=MagickTrue;
13669        mng_info->ping_exclude_iCCP=MagickTrue;
13670        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13671        mng_info->ping_exclude_oFFs=MagickTrue;
13672        mng_info->ping_exclude_pHYs=MagickTrue;
13673        mng_info->ping_exclude_sRGB=MagickTrue;
13674        mng_info->ping_exclude_tEXt=MagickTrue;
13675        mng_info->ping_exclude_tRNS=MagickTrue;
13676        mng_info->ping_exclude_vpAg=MagickTrue;
13677        mng_info->ping_exclude_zCCP=MagickTrue;
13678        mng_info->ping_exclude_zTXt=MagickTrue;
13679 
13680        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13681      }
13682 
13683     if (status == MagickFalse)
13684       {
13685         MngInfoFreeStruct(mng_info,&have_mng_structure);
13686         (void) CloseBlob(image);
13687         return(MagickFalse);
13688       }
13689     (void) CatchImageException(image);
13690     if (GetNextImageInList(image) == (Image *) NULL)
13691       break;
13692     image=SyncNextImageInList(image);
13693     status=SetImageProgress(image,SaveImagesTag,scene++,
13694       GetImageListLength(image));
13695 
13696     if (status == MagickFalse)
13697       break;
13698 
13699   } while (mng_info->adjoin);
13700 
13701   if (write_mng)
13702     {
13703       while (GetPreviousImageInList(image) != (Image *) NULL)
13704         image=GetPreviousImageInList(image);
13705       /*
13706         Write the MEND chunk.
13707       */
13708       (void) WriteBlobMSBULong(image,0x00000000L);
13709       PNGType(chunk,mng_MEND);
13710       LogPNGChunk(logging,mng_MEND,0L);
13711       (void) WriteBlob(image,4,chunk);
13712       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13713     }
13714   /*
13715     Relinquish resources.
13716   */
13717   (void) CloseBlob(image);
13718   MngInfoFreeStruct(mng_info,&have_mng_structure);
13719 
13720   if (logging != MagickFalse)
13721     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13722 
13723   return(MagickTrue);
13724 }
13725 #else /* PNG_LIBPNG_VER > 10011 */
13726 
WritePNGImage(const ImageInfo * image_info,Image * image)13727 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13728 {
13729   (void) image;
13730   printf("Your PNG library is too old: You have libpng-%s\n",
13731      PNG_LIBPNG_VER_STRING);
13732 
13733   ThrowBinaryException(CoderError,"PNG library is too old",
13734      image_info->filename);
13735 }
13736 
WriteMNGImage(const ImageInfo * image_info,Image * image)13737 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13738 {
13739   return(WritePNGImage(image_info,image));
13740 }
13741 #endif /* PNG_LIBPNG_VER > 10011 */
13742 #endif
13743