• 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-2021 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    https://imagemagick.org/script/license.php                               %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 
40 #define IM
41 
42 
43 /*
44   Include declarations.
45 */
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/channel.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colormap-private.h"
57 #include "MagickCore/colorspace.h"
58 #include "MagickCore/colorspace-private.h"
59 #include "MagickCore/constitute.h"
60 #include "MagickCore/enhance.h"
61 #include "MagickCore/exception.h"
62 #include "MagickCore/exception-private.h"
63 #include "MagickCore/geometry.h"
64 #include "MagickCore/histogram.h"
65 #include "MagickCore/image.h"
66 #include "MagickCore/image-private.h"
67 #include "MagickCore/layer.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/magick-private.h"
72 #include "MagickCore/memory_.h"
73 #include "MagickCore/memory-private.h"
74 #include "MagickCore/module.h"
75 #include "MagickCore/monitor.h"
76 #include "MagickCore/monitor-private.h"
77 #include "MagickCore/option.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/profile.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resource_.h"
84 #include "MagickCore/semaphore.h"
85 #include "MagickCore/quantum-private.h"
86 #include "MagickCore/static.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/timer-private.h"
91 #include "MagickCore/transform.h"
92 #include "MagickCore/utility.h"
93 #if defined(MAGICKCORE_PNG_DELEGATE)
94 
95 /* Suppress libpng pedantic warnings that were added in
96  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
97  * migration to libpng-1.5, remove these defines and then
98  * fix any code that generates warnings.
99  */
100 /* #define PNG_DEPRECATED   Use of this function is deprecated */
101 /* #define PNG_USE_RESULT   The result of this function must be checked */
102 /* #define PNG_NORETURN     This function does not return */
103 /* #define PNG_ALLOCATED    The result of the function is new memory */
104 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
105 
106 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
107 #define PNG_PTR_NORETURN
108 
109 #include <png.h>
110 #include <zlib.h>
111 
112 /* ImageMagick differences */
113 #define first_scene scene
114 
115 #if PNG_LIBPNG_VER > 10011
116 /*
117   Optional declarations. Define or undefine them as you like.
118 */
119 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
120 
121 /*
122   Features under construction.  Define these to work on them.
123 */
124 #undef MNG_OBJECT_BUFFERS
125 #undef MNG_BASI_SUPPORTED
126 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
127 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
128 #if defined(MAGICKCORE_JPEG_DELEGATE)
129 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
130 #endif
131 #if !defined(RGBColorMatchExact)
132 #define IsPNGColorEqual(color,target) \
133        (((color).red == (target).red) && \
134         ((color).green == (target).green) && \
135         ((color).blue == (target).blue))
136 #endif
137 
138 /* Table of recognized sRGB ICC profiles */
139 struct sRGB_info_struct
140 {
141     png_uint_32 len;
142     png_uint_32 crc;
143     png_byte intent;
144 };
145 
146 const struct sRGB_info_struct sRGB_info[] =
147 {
148     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
149     { 3048, 0x3b8772b9UL, 0},
150 
151     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
152     { 3052, 0x427ebb21UL, 1},
153 
154     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
155     {60988, 0x306fd8aeUL, 0},
156 
157     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
158      {60960, 0xbbef7812UL, 0},
159 
160     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
161      { 3024, 0x5d5129ceUL, 1},
162 
163      /* HP-Microsoft sRGB v2 perceptual */
164      { 3144, 0x182ea552UL, 0},
165 
166      /* HP-Microsoft sRGB v2 media-relative */
167      { 3144, 0xf29e526dUL, 1},
168 
169      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
170      {  524, 0xd4938c39UL, 0},
171 
172      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
173      { 3212, 0x034af5a1UL, 0},
174 
175      /* Not recognized */
176      {    0, 0x00000000UL, 0},
177 };
178 
179 /* Macros for left-bit-replication to ensure that pixels
180  * and PixelInfos all have the same image->depth, and for use
181  * in PNG8 quantization.
182  */
183 
184 /* LBR01: Replicate top bit */
185 
186 #define LBR01PacketRed(pixelpacket) \
187      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
188         0 : QuantumRange);
189 
190 #define LBR01PacketGreen(pixelpacket) \
191      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
192         0 : QuantumRange);
193 
194 #define LBR01PacketBlue(pixelpacket) \
195      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
196         0 : QuantumRange);
197 
198 #define LBR01PacketAlpha(pixelpacket) \
199      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
200         0 : QuantumRange);
201 
202 #define LBR01PacketRGB(pixelpacket) \
203         { \
204         LBR01PacketRed((pixelpacket)); \
205         LBR01PacketGreen((pixelpacket)); \
206         LBR01PacketBlue((pixelpacket)); \
207         }
208 
209 #define LBR01PacketRGBA(pixelpacket) \
210         { \
211         LBR01PacketRGB((pixelpacket)); \
212         LBR01PacketAlpha((pixelpacket)); \
213         }
214 
215 #define LBR01PixelRed(pixel) \
216         (SetPixelRed(image, \
217         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
218         0 : QuantumRange,(pixel)));
219 
220 #define LBR01PixelGreen(pixel) \
221         (SetPixelGreen(image, \
222         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
223         0 : QuantumRange,(pixel)));
224 
225 #define LBR01PixelBlue(pixel) \
226         (SetPixelBlue(image, \
227         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
228         0 : QuantumRange,(pixel)));
229 
230 #define LBR01PixelAlpha(pixel) \
231         (SetPixelAlpha(image, \
232         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
233         0 : QuantumRange,(pixel)));
234 
235 #define LBR01PixelRGB(pixel) \
236         { \
237         LBR01PixelRed((pixel)); \
238         LBR01PixelGreen((pixel)); \
239         LBR01PixelBlue((pixel)); \
240         }
241 
242 #define LBR01PixelRGBA(pixel) \
243         { \
244         LBR01PixelRGB((pixel)); \
245         LBR01PixelAlpha((pixel)); \
246         }
247 
248 /* LBR02: Replicate top 2 bits */
249 
250 #define LBR02PacketRed(pixelpacket) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
253      (pixelpacket).red=ScaleCharToQuantum( \
254        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255    }
256 #define LBR02PacketGreen(pixelpacket) \
257    { \
258      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
259      (pixelpacket).green=ScaleCharToQuantum( \
260        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261    }
262 #define LBR02PacketBlue(pixelpacket) \
263    { \
264      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
265      (pixelpacket).blue=ScaleCharToQuantum( \
266        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267    }
268 #define LBR02PacketAlpha(pixelpacket) \
269    { \
270      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
271      (pixelpacket).alpha=ScaleCharToQuantum( \
272        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
273    }
274 
275 #define LBR02PacketRGB(pixelpacket) \
276         { \
277         LBR02PacketRed((pixelpacket)); \
278         LBR02PacketGreen((pixelpacket)); \
279         LBR02PacketBlue((pixelpacket)); \
280         }
281 
282 #define LBR02PacketRGBA(pixelpacket) \
283         { \
284         LBR02PacketRGB((pixelpacket)); \
285         LBR02PacketAlpha((pixelpacket)); \
286         }
287 
288 #define LBR02PixelRed(pixel) \
289    { \
290      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
291        & 0xc0; \
292      SetPixelRed(image, ScaleCharToQuantum( \
293        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
294        (pixel)); \
295    }
296 #define LBR02PixelGreen(pixel) \
297    { \
298      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
299        & 0xc0; \
300      SetPixelGreen(image, ScaleCharToQuantum( \
301        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
302        (pixel)); \
303    }
304 #define LBR02PixelBlue(pixel) \
305    { \
306      unsigned char lbr_bits= \
307        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
308      SetPixelBlue(image, ScaleCharToQuantum( \
309        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
310        (pixel)); \
311    }
312 #define LBR02PixelAlpha(pixel) \
313    { \
314      unsigned char lbr_bits= \
315        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
316      SetPixelAlpha(image, ScaleCharToQuantum( \
317        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
318        (pixel) ); \
319    }
320 
321 #define LBR02PixelRGB(pixel) \
322         { \
323         LBR02PixelRed((pixel)); \
324         LBR02PixelGreen((pixel)); \
325         LBR02PixelBlue((pixel)); \
326         }
327 
328 #define LBR02PixelRGBA(pixel) \
329         { \
330         LBR02PixelRGB((pixel)); \
331         LBR02PixelAlpha((pixel)); \
332         }
333 
334 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
335    PNG8 quantization) */
336 
337 #define LBR03PacketRed(pixelpacket) \
338    { \
339      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
340      (pixelpacket).red=ScaleCharToQuantum( \
341        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342    }
343 #define LBR03PacketGreen(pixelpacket) \
344    { \
345      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
346      (pixelpacket).green=ScaleCharToQuantum( \
347        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348    }
349 #define LBR03PacketBlue(pixelpacket) \
350    { \
351      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
352      (pixelpacket).blue=ScaleCharToQuantum( \
353        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
354    }
355 
356 #define LBR03PacketRGB(pixelpacket) \
357         { \
358         LBR03PacketRed((pixelpacket)); \
359         LBR03PacketGreen((pixelpacket)); \
360         LBR03PacketBlue((pixelpacket)); \
361         }
362 
363 #define LBR03PixelRed(pixel) \
364    { \
365      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
366        & 0xe0; \
367      SetPixelRed(image, ScaleCharToQuantum( \
368        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
369    }
370 #define LBR03Green(pixel) \
371    { \
372      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
373        & 0xe0; \
374      SetPixelGreen(image, ScaleCharToQuantum( \
375        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
376    }
377 #define LBR03Blue(pixel) \
378    { \
379      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
380        & 0xe0; \
381      SetPixelBlue(image, ScaleCharToQuantum( \
382        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
383    }
384 
385 #define LBR03RGB(pixel) \
386         { \
387         LBR03PixelRed((pixel)); \
388         LBR03Green((pixel)); \
389         LBR03Blue((pixel)); \
390         }
391 
392 /* LBR04: Replicate top 4 bits */
393 
394 #define LBR04PacketRed(pixelpacket) \
395    { \
396      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
397      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
398    }
399 #define LBR04PacketGreen(pixelpacket) \
400    { \
401      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
402      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
403    }
404 #define LBR04PacketBlue(pixelpacket) \
405    { \
406      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
407      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
408    }
409 #define LBR04PacketAlpha(pixelpacket) \
410    { \
411      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
412      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
413    }
414 
415 #define LBR04PacketRGB(pixelpacket) \
416         { \
417         LBR04PacketRed((pixelpacket)); \
418         LBR04PacketGreen((pixelpacket)); \
419         LBR04PacketBlue((pixelpacket)); \
420         }
421 
422 #define LBR04PacketRGBA(pixelpacket) \
423         { \
424         LBR04PacketRGB((pixelpacket)); \
425         LBR04PacketAlpha((pixelpacket)); \
426         }
427 
428 #define LBR04PixelRed(pixel) \
429    { \
430      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
431        & 0xf0; \
432      SetPixelRed(image,\
433        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
434    }
435 #define LBR04PixelGreen(pixel) \
436    { \
437      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
438        & 0xf0; \
439      SetPixelGreen(image,\
440        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
441    }
442 #define LBR04PixelBlue(pixel) \
443    { \
444      unsigned char lbr_bits= \
445        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
446      SetPixelBlue(image,\
447        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
448    }
449 #define LBR04PixelAlpha(pixel) \
450    { \
451      unsigned char lbr_bits= \
452        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
453      SetPixelAlpha(image,\
454        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
455    }
456 
457 #define LBR04PixelRGB(pixel) \
458         { \
459         LBR04PixelRed((pixel)); \
460         LBR04PixelGreen((pixel)); \
461         LBR04PixelBlue((pixel)); \
462         }
463 
464 #define LBR04PixelRGBA(pixel) \
465         { \
466         LBR04PixelRGB((pixel)); \
467         LBR04PixelAlpha((pixel)); \
468         }
469 
470 /*
471   Establish thread safety.
472   setjmp/longjmp is claimed to be safe on these platforms:
473   setjmp/longjmp is alleged to be unsafe on these platforms:
474 */
475 #ifdef PNG_SETJMP_SUPPORTED
476 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
477 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
478 # endif
479 
480 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
481 static SemaphoreInfo
482   *ping_semaphore = (SemaphoreInfo *) NULL;
483 # endif
484 #endif
485 
486 /*
487   This temporary until I set up malloc'ed object attributes array.
488   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
489   waste more memory.
490 */
491 #define MNG_MAX_OBJECTS 256
492 
493 /*
494   If this not defined, spec is interpreted strictly.  If it is
495   defined, an attempt will be made to recover from some errors,
496   including
497       o global PLTE too short
498 */
499 #undef MNG_LOOSE
500 
501 /*
502   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
503   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
504   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
505   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
506   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
507   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
508   will be enabled by default in libpng-1.2.0.
509 */
510 #ifdef PNG_MNG_FEATURES_SUPPORTED
511 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
512 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
513 #  endif
514 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
515 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
516 #  endif
517 #endif
518 
519 /*
520   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
521   This macro is only defined in libpng-1.0.3 and later.
522   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
523 */
524 #ifndef PNG_UINT_31_MAX
525 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
526 #endif
527 
528 /*
529   Constant strings for known chunk types.  If you need to add a chunk,
530   add a string holding the name here.   To make the code more
531   portable, we use ASCII numbers like this, not characters.
532 */
533 
534 static const png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
535 static const png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
536 static const png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
537 static const png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
538 static const png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
539 static const png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
540 static const png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
541 static const png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
542 static const png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
543 static const png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
544 static const png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
545 static const png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
546 static const png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
547 static const png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
548 static const png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
549 static const png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
550 static const png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
551 static const png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
552 static const png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
553 static const png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
554 static const png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
555 static const png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
556 static const png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
557 static const png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
558 static const png_byte mng_caNv[5]={ 99,  97,  78, 118, (png_byte) '\0'};
559 static const png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
560 static const png_byte mng_eXIf[5]={101,  88,  73, 102, (png_byte) '\0'};
561 static const png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
562 static const png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
563 static const png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
564 static const png_byte mng_orNT[5]={111, 114,  78,  84, (png_byte) '\0'};
565 static const png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
566 static const png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
567 static const png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
568 static const png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
569 static const png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
570 static const png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
571 
572 #if defined(JNG_SUPPORTED)
573 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
574 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
575 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
576 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
577 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
578 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
579 #endif
580 
581 #if 0
582 /* Other known chunks that are not yet supported by ImageMagick: */
583 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
584 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
585 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
586 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
587 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
588 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
589 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
590 #endif
591 
592 typedef struct _MngBox
593 {
594   long
595     left,
596     right,
597     top,
598     bottom;
599 } MngBox;
600 
601 typedef struct _MngPair
602 {
603   volatile long
604     a,
605     b;
606 } MngPair;
607 
608 #ifdef MNG_OBJECT_BUFFERS
609 typedef struct _MngBuffer
610 {
611 
612   size_t
613     height,
614     width;
615 
616   Image
617     *image;
618 
619   png_color
620     plte[256];
621 
622   int
623     reference_count;
624 
625   unsigned char
626     alpha_sample_depth,
627     compression_method,
628     color_type,
629     concrete,
630     filter_method,
631     frozen,
632     image_type,
633     interlace_method,
634     pixel_sample_depth,
635     plte_length,
636     sample_depth,
637     viewable;
638 } MngBuffer;
639 #endif
640 
641 typedef struct _MngInfo
642 {
643 
644 #ifdef MNG_OBJECT_BUFFERS
645   MngBuffer
646     *ob[MNG_MAX_OBJECTS];
647 #endif
648 
649   Image *
650     image;
651 
652   RectangleInfo
653     page;
654 
655   int
656     adjoin,
657 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
658     bytes_in_read_buffer,
659     found_empty_plte,
660 #endif
661     equal_backgrounds,
662     equal_chrms,
663     equal_gammas,
664 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
665     defined(PNG_MNG_FEATURES_SUPPORTED)
666     equal_palettes,
667 #endif
668     equal_physs,
669     equal_srgbs,
670     framing_mode,
671     have_global_bkgd,
672     have_global_chrm,
673     have_global_gama,
674     have_global_phys,
675     have_global_sbit,
676     have_global_srgb,
677     have_saved_bkgd_index,
678     have_write_global_chrm,
679     have_write_global_gama,
680     have_write_global_plte,
681     have_write_global_srgb,
682     need_fram,
683     object_id,
684     old_framing_mode,
685     saved_bkgd_index;
686 
687   int
688     new_number_colors;
689 
690   ssize_t
691     image_found,
692     loop_count[256],
693     loop_iteration[256],
694     scenes_found,
695     x_off[MNG_MAX_OBJECTS],
696     y_off[MNG_MAX_OBJECTS];
697 
698   MngBox
699     clip,
700     frame,
701     image_box,
702     object_clip[MNG_MAX_OBJECTS];
703 
704   unsigned char
705     /* These flags could be combined into one byte */
706     exists[MNG_MAX_OBJECTS],
707     frozen[MNG_MAX_OBJECTS],
708     loop_active[256],
709     invisible[MNG_MAX_OBJECTS],
710     viewable[MNG_MAX_OBJECTS];
711 
712   MagickOffsetType
713     loop_jump[256];
714 
715   png_colorp
716     global_plte;
717 
718   png_color_8
719     global_sbit;
720 
721   png_byte
722 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
723     read_buffer[8],
724 #endif
725     global_trns[256];
726 
727   float
728     global_gamma;
729 
730   ChromaticityInfo
731     global_chrm;
732 
733   RenderingIntent
734     global_srgb_intent;
735 
736   unsigned long
737     delay,
738     global_plte_length,
739     global_trns_length,
740     global_x_pixels_per_unit,
741     global_y_pixels_per_unit,
742     mng_width,
743     mng_height,
744     ticks_per_second;
745 
746   MagickBooleanType
747     need_blob;
748 
749   unsigned int
750     IsPalette,
751     global_phys_unit_type,
752     basi_warning,
753     clon_warning,
754     dhdr_warning,
755     jhdr_warning,
756     magn_warning,
757     past_warning,
758     phyg_warning,
759     phys_warning,
760     sbit_warning,
761     show_warning,
762     mng_type,
763     write_mng,
764     write_png_colortype,
765     write_png_depth,
766     write_png_compression_level,
767     write_png_compression_strategy,
768     write_png_compression_filter,
769     write_png8,
770     write_png24,
771     write_png32,
772     write_png48,
773     write_png64;
774 
775 #ifdef MNG_BASI_SUPPORTED
776   unsigned long
777     basi_width,
778     basi_height;
779 
780   unsigned int
781     basi_depth,
782     basi_color_type,
783     basi_compression_method,
784     basi_filter_type,
785     basi_interlace_method,
786     basi_red,
787     basi_green,
788     basi_blue,
789     basi_alpha,
790     basi_viewable;
791 #endif
792 
793   png_uint_16
794     magn_first,
795     magn_last,
796     magn_mb,
797     magn_ml,
798     magn_mr,
799     magn_mt,
800     magn_mx,
801     magn_my,
802     magn_methx,
803     magn_methy;
804 
805   PixelInfo
806     mng_global_bkgd;
807 
808   /* Added at version 6.6.6-7 */
809   MagickBooleanType
810     ping_exclude_bKGD,
811     ping_exclude_cHRM,
812     ping_exclude_date,
813     ping_exclude_eXIf,
814     ping_exclude_EXIF,
815     ping_exclude_gAMA,
816     ping_exclude_iCCP,
817     /* ping_exclude_iTXt, */
818     ping_exclude_oFFs,
819     ping_exclude_pHYs,
820     ping_exclude_sRGB,
821     ping_exclude_tEXt,
822     ping_exclude_tRNS,
823     ping_exclude_caNv,
824     ping_exclude_zCCP, /* hex-encoded iCCP */
825     ping_exclude_zTXt,
826     ping_preserve_colormap,
827   /* Added at version 6.8.5-7 */
828     ping_preserve_iCCP,
829   /* Added at version 6.8.9-9 */
830     ping_exclude_tIME;
831 
832 } MngInfo;
833 #endif /* VER */
834 
835 /*
836   Forward declarations.
837 */
838 static MagickBooleanType
839   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
840 
841 static MagickBooleanType
842   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
843 
844 #if defined(JNG_SUPPORTED)
845 static MagickBooleanType
846   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
847 #endif
848 
849 #if PNG_LIBPNG_VER > 10011
850 
851 
852 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
853 static MagickBooleanType
LosslessReduceDepthOK(Image * image,ExceptionInfo * exception)854 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
855 {
856     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
857      *
858      * This is true if the high byte and the next highest byte of
859      * each sample of the image, the colormap, and the background color
860      * are equal to each other.  We check this by seeing if the samples
861      * are unchanged when we scale them down to 8 and back up to Quantum.
862      *
863      * We don't use the method GetImageDepth() because it doesn't check
864      * background and doesn't handle PseudoClass specially.
865      */
866 
867 #define QuantumToCharToQuantumEqQuantum(quantum) \
868  ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
869 
870     MagickBooleanType
871       ok_to_reduce=MagickFalse;
872 
873     if (image->depth >= 16)
874       {
875 
876         const Quantum
877           *p;
878 
879         ok_to_reduce=
880            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
881            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
882            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
883            MagickTrue : MagickFalse;
884 
885         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
886           {
887             int indx;
888 
889             for (indx=0; indx < (ssize_t) image->colors; indx++)
890               {
891                 ok_to_reduce=(
892                    QuantumToCharToQuantumEqQuantum(
893                    image->colormap[indx].red) &&
894                    QuantumToCharToQuantumEqQuantum(
895                    image->colormap[indx].green) &&
896                    QuantumToCharToQuantumEqQuantum(
897                    image->colormap[indx].blue)) ?
898                    MagickTrue : MagickFalse;
899 
900                 if (ok_to_reduce == MagickFalse)
901                    break;
902               }
903           }
904 
905         if ((ok_to_reduce != MagickFalse) &&
906             (image->storage_class != PseudoClass))
907           {
908             ssize_t
909               y;
910 
911             ssize_t
912               x;
913 
914             for (y=0; y < (ssize_t) image->rows; y++)
915             {
916               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
917 
918               if (p == (const Quantum *) NULL)
919                 {
920                   ok_to_reduce = MagickFalse;
921                   break;
922                 }
923 
924               for (x=(ssize_t) image->columns-1; x >= 0; x--)
925               {
926                 ok_to_reduce=
927                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
928                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
929                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
930                    MagickTrue : MagickFalse;
931 
932                 if (ok_to_reduce == MagickFalse)
933                   break;
934 
935                 p+=GetPixelChannels(image);
936               }
937               if (x >= 0)
938                 break;
939             }
940           }
941 
942         if (ok_to_reduce != MagickFalse)
943           {
944             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
945                 "    OK to reduce PNG bit depth to 8 without loss of info");
946           }
947         else
948           {
949             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
950                 "    Not OK to reduce PNG bit depth to 8 without losing info");
951           }
952       }
953 
954     return ok_to_reduce;
955 }
956 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
957 
PngColorTypeToString(const unsigned int color_type)958 static const char *PngColorTypeToString(const unsigned int color_type)
959 {
960   const char
961     *result = "Unknown";
962 
963   switch (color_type)
964     {
965     case PNG_COLOR_TYPE_GRAY:
966       result = "Gray";
967       break;
968     case PNG_COLOR_TYPE_GRAY_ALPHA:
969       result = "Gray+Alpha";
970       break;
971     case PNG_COLOR_TYPE_PALETTE:
972       result = "Palette";
973       break;
974     case PNG_COLOR_TYPE_RGB:
975       result = "RGB";
976       break;
977     case PNG_COLOR_TYPE_RGB_ALPHA:
978       result = "RGB+Alpha";
979       break;
980     }
981 
982   return result;
983 }
984 
985 static int
Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)986 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
987 {
988   switch (orientation)
989   {
990     /* Convert to Exif orientations as defined in "Exchangeable image file
991      * format for digital still cameras: Exif Version 2.31",
992      * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
993      */
994 
995     case TopLeftOrientation:
996        return 1;
997     case TopRightOrientation:
998        return 2;
999     case BottomRightOrientation:
1000        return 3;
1001     case BottomLeftOrientation:
1002        return 4;
1003     case LeftTopOrientation:
1004        return 5;
1005     case RightTopOrientation:
1006        return 6;
1007     case RightBottomOrientation:
1008        return 7;
1009     case LeftBottomOrientation:
1010        return 8;
1011     case UndefinedOrientation:
1012     default:
1013        return 0;
1014   }
1015 }
1016 static OrientationType
Magick_Orientation_from_Exif_Orientation(const int orientation)1017 Magick_Orientation_from_Exif_Orientation(const int orientation)
1018 {
1019   switch (orientation)
1020   {
1021     case 1:
1022       return TopLeftOrientation;
1023     case 2:
1024       return TopRightOrientation;
1025     case 3:
1026       return BottomRightOrientation;
1027     case 4:
1028       return BottomLeftOrientation;
1029     case 5:
1030       return LeftTopOrientation;
1031     case 6:
1032       return RightTopOrientation;
1033     case 7:
1034       return RightBottomOrientation;
1035     case 8:
1036       return LeftBottomOrientation;
1037     case 0:
1038     default:
1039       return UndefinedOrientation;
1040   }
1041 }
1042 
1043 static int
Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)1044 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1045 {
1046   switch (intent)
1047   {
1048     case PerceptualIntent:
1049        return 0;
1050 
1051     case RelativeIntent:
1052        return 1;
1053 
1054     case SaturationIntent:
1055        return 2;
1056 
1057     case AbsoluteIntent:
1058        return 3;
1059 
1060     default:
1061        return -1;
1062   }
1063 }
1064 
1065 static RenderingIntent
Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)1066 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1067 {
1068   switch (ping_intent)
1069   {
1070     case 0:
1071       return PerceptualIntent;
1072 
1073     case 1:
1074       return RelativeIntent;
1075 
1076     case 2:
1077       return SaturationIntent;
1078 
1079     case 3:
1080       return AbsoluteIntent;
1081 
1082     default:
1083       return UndefinedIntent;
1084     }
1085 }
1086 
1087 static const char *
Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)1088 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1089 {
1090   switch (ping_intent)
1091   {
1092     case 0:
1093       return "Perceptual Intent";
1094 
1095     case 1:
1096       return "Relative Intent";
1097 
1098     case 2:
1099       return "Saturation Intent";
1100 
1101     case 3:
1102       return "Absolute Intent";
1103 
1104     default:
1105       return "Undefined Intent";
1106     }
1107 }
1108 
1109 static const char *
Magick_ColorType_from_PNG_ColorType(const int ping_colortype)1110 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1111 {
1112   switch (ping_colortype)
1113   {
1114     case 0:
1115       return "Grayscale";
1116 
1117     case 2:
1118       return "Truecolor";
1119 
1120     case 3:
1121       return "Indexed";
1122 
1123     case 4:
1124       return "GrayAlpha";
1125 
1126     case 6:
1127       return "RGBA";
1128 
1129     default:
1130       return "UndefinedColorType";
1131     }
1132 }
1133 
1134 #endif /* PNG_LIBPNG_VER > 10011 */
1135 #endif /* MAGICKCORE_PNG_DELEGATE */
1136 
1137 /*
1138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1139 %                                                                             %
1140 %                                                                             %
1141 %                                                                             %
1142 %   I s M N G                                                                 %
1143 %                                                                             %
1144 %                                                                             %
1145 %                                                                             %
1146 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1147 %
1148 %  IsMNG() returns MagickTrue if the image format type, identified by the
1149 %  magick string, is MNG.
1150 %
1151 %  The format of the IsMNG method is:
1152 %
1153 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1154 %
1155 %  A description of each parameter follows:
1156 %
1157 %    o magick: compare image format pattern against these bytes.
1158 %
1159 %    o length: Specifies the length of the magick string.
1160 %
1161 %
1162 */
IsMNG(const unsigned char * magick,const size_t length)1163 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1164 {
1165   if (length < 8)
1166     return(MagickFalse);
1167 
1168   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1169     return(MagickTrue);
1170 
1171   return(MagickFalse);
1172 }
1173 
1174 /*
1175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176 %                                                                             %
1177 %                                                                             %
1178 %                                                                             %
1179 %   I s J N G                                                                 %
1180 %                                                                             %
1181 %                                                                             %
1182 %                                                                             %
1183 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1184 %
1185 %  IsJNG() returns MagickTrue if the image format type, identified by the
1186 %  magick string, is JNG.
1187 %
1188 %  The format of the IsJNG method is:
1189 %
1190 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1191 %
1192 %  A description of each parameter follows:
1193 %
1194 %    o magick: compare image format pattern against these bytes.
1195 %
1196 %    o length: Specifies the length of the magick string.
1197 %
1198 %
1199 */
IsJNG(const unsigned char * magick,const size_t length)1200 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1201 {
1202   if (length < 8)
1203     return(MagickFalse);
1204 
1205   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1206     return(MagickTrue);
1207 
1208   return(MagickFalse);
1209 }
1210 
1211 /*
1212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1213 %                                                                             %
1214 %                                                                             %
1215 %                                                                             %
1216 %   I s P N G                                                                 %
1217 %                                                                             %
1218 %                                                                             %
1219 %                                                                             %
1220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1221 %
1222 %  IsPNG() returns MagickTrue if the image format type, identified by the
1223 %  magick string, is PNG.
1224 %
1225 %  The format of the IsPNG method is:
1226 %
1227 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1228 %
1229 %  A description of each parameter follows:
1230 %
1231 %    o magick: compare image format pattern against these bytes.
1232 %
1233 %    o length: Specifies the length of the magick string.
1234 %
1235 */
IsPNG(const unsigned char * magick,const size_t length)1236 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1237 {
1238   if (length < 8)
1239     return(MagickFalse);
1240 
1241   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1242     return(MagickTrue);
1243 
1244   return(MagickFalse);
1245 }
1246 
1247 #if defined(MAGICKCORE_PNG_DELEGATE)
1248 #if defined(__cplusplus) || defined(c_plusplus)
1249 extern "C" {
1250 #endif
1251 
1252 #if (PNG_LIBPNG_VER > 10011)
WriteBlobMSBULong(Image * image,const size_t value)1253 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1254 {
1255   unsigned char
1256     buffer[4];
1257 
1258   assert(image != (Image *) NULL);
1259   assert(image->signature == MagickCoreSignature);
1260   buffer[0]=(unsigned char) (value >> 24);
1261   buffer[1]=(unsigned char) (value >> 16);
1262   buffer[2]=(unsigned char) (value >> 8);
1263   buffer[3]=(unsigned char) value;
1264   return((size_t) WriteBlob(image,4,buffer));
1265 }
1266 
PNGLong(png_bytep p,png_uint_32 value)1267 static void PNGLong(png_bytep p,png_uint_32 value)
1268 {
1269   *p++=(png_byte) ((value >> 24) & 0xff);
1270   *p++=(png_byte) ((value >> 16) & 0xff);
1271   *p++=(png_byte) ((value >> 8) & 0xff);
1272   *p++=(png_byte) (value & 0xff);
1273 }
1274 
PNGsLong(png_bytep p,png_int_32 value)1275 static void PNGsLong(png_bytep p,png_int_32 value)
1276 {
1277   *p++=(png_byte) ((value >> 24) & 0xff);
1278   *p++=(png_byte) ((value >> 16) & 0xff);
1279   *p++=(png_byte) ((value >> 8) & 0xff);
1280   *p++=(png_byte) (value & 0xff);
1281 }
1282 
PNGShort(png_bytep p,png_uint_16 value)1283 static void PNGShort(png_bytep p,png_uint_16 value)
1284 {
1285   *p++=(png_byte) ((value >> 8) & 0xff);
1286   *p++=(png_byte) (value & 0xff);
1287 }
1288 
PNGType(png_bytep p,const png_byte * type)1289 static void PNGType(png_bytep p,const png_byte *type)
1290 {
1291   (void) memcpy(p,type,4*sizeof(png_byte));
1292 }
1293 
LogPNGChunk(MagickBooleanType logging,const png_byte * type,size_t length)1294 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1295    size_t length)
1296 {
1297   if (logging != MagickFalse)
1298     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1299       "  Writing %c%c%c%c chunk, length: %.20g",
1300       type[0],type[1],type[2],type[3],(double) length);
1301 }
1302 #endif /* PNG_LIBPNG_VER > 10011 */
1303 
1304 #if defined(__cplusplus) || defined(c_plusplus)
1305 }
1306 #endif
1307 
1308 #if PNG_LIBPNG_VER > 10011
1309 /*
1310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311 %                                                                             %
1312 %                                                                             %
1313 %                                                                             %
1314 %   R e a d P N G I m a g e                                                   %
1315 %                                                                             %
1316 %                                                                             %
1317 %                                                                             %
1318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1319 %
1320 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1321 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1322 %  allocates the memory necessary for the new Image structure and returns a
1323 %  pointer to the new image or set of images.
1324 %
1325 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1326 %
1327 %  The format of the ReadPNGImage method is:
1328 %
1329 %     Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1330 %
1331 %  A description of each parameter follows:
1332 %
1333 %    o image_info: the image info.
1334 %
1335 %    o exception: return any errors or warnings in this structure.
1336 %
1337 %  To do, more or less in chronological order (as of version 5.5.2,
1338 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1339 %
1340 %    Get 16-bit cheap transparency working.
1341 %
1342 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1343 %
1344 %    Preserve all unknown and not-yet-handled known chunks found in input
1345 %    PNG file and copy them into output PNG files according to the PNG
1346 %    copying rules.
1347 %
1348 %    (At this point, PNG encoding should be in full MNG compliance)
1349 %
1350 %    Provide options for choice of background to use when the MNG BACK
1351 %    chunk is not present or is not mandatory (i.e., leave transparent,
1352 %    user specified, MNG BACK, PNG bKGD)
1353 %
1354 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1355 %    efficiently by linking in the duplicate frames.].
1356 %
1357 %    Decode and act on the MHDR simplicity profile (offer option to reject
1358 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1359 %
1360 %    Upgrade to full MNG without Delta-PNG.
1361 %
1362 %        o  BACK [done a while ago except for background image ID]
1363 %        o  MOVE [done 15 May 1999]
1364 %        o  CLIP [done 15 May 1999]
1365 %        o  DISC [done 19 May 1999]
1366 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1367 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1368 %        o  SHOW
1369 %        o  PAST
1370 %        o  BASI
1371 %        o  MNG-level tEXt/iTXt/zTXt
1372 %        o  pHYg
1373 %        o  pHYs
1374 %        o  sBIT
1375 %        o  bKGD
1376 %        o  iTXt (wait for libpng implementation).
1377 %
1378 %    Use the scene signature to discover when an identical scene is
1379 %    being reused, and just point to the original image->exception instead
1380 %    of storing another set of pixels.  This not specific to MNG
1381 %    but could be applied generally.
1382 %
1383 %    Upgrade to full MNG with Delta-PNG.
1384 %
1385 %    JNG tEXt/iTXt/zTXt
1386 %
1387 %    We will not attempt to read files containing the CgBI chunk.
1388 %    They are really Xcode files meant for display on the iPhone.
1389 %    These are not valid PNG files and it is impossible to recover
1390 %    the original PNG from files that have been converted to Xcode-PNG,
1391 %    since irretrievable loss of color data has occurred due to the
1392 %    use of premultiplied alpha.
1393 */
1394 
1395 #if defined(__cplusplus) || defined(c_plusplus)
1396 extern "C" {
1397 #endif
1398 
1399 /*
1400   This the function that does the actual reading of data.  It is
1401   the same as the one supplied in libpng, except that it receives the
1402   datastream from the ReadBlob() function instead of standard input.
1403 */
png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1404 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1405 {
1406   Image
1407     *image;
1408 
1409   image=(Image *) png_get_io_ptr(png_ptr);
1410   if (length != 0)
1411     {
1412       png_size_t
1413         check;
1414 
1415       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1416       if (check != length)
1417         {
1418           char
1419             msg[MagickPathExtent];
1420 
1421           (void) FormatLocaleString(msg,MagickPathExtent,
1422             "Expected %.20g bytes; found %.20g bytes",(double) length,
1423             (double) check);
1424           png_warning(png_ptr,msg);
1425           png_error(png_ptr,"Read Exception");
1426         }
1427     }
1428 }
1429 
1430 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1431     !defined(PNG_MNG_FEATURES_SUPPORTED)
1432 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1433  * older than libpng-1.0.3a, which was the first to allow the empty
1434  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1435  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1436  * encountered after an empty PLTE, so we have to look ahead for bKGD
1437  * chunks and remove them from the datastream that is passed to libpng,
1438  * and store their contents for later use.
1439  */
mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)1440 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1441 {
1442   MngInfo
1443     *mng_info;
1444 
1445   Image
1446     *image;
1447 
1448   png_size_t
1449     check;
1450 
1451   ssize_t
1452     i;
1453 
1454   i=0;
1455   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1456   image=(Image *) mng_info->image;
1457   while (mng_info->bytes_in_read_buffer && length)
1458   {
1459     data[i]=mng_info->read_buffer[i];
1460     mng_info->bytes_in_read_buffer--;
1461     length--;
1462     i++;
1463   }
1464   if (length != 0)
1465     {
1466       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1467 
1468       if (check != length)
1469         png_error(png_ptr,"Read Exception");
1470 
1471       if (length == 4)
1472         {
1473           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1474               (data[3] == 0))
1475             {
1476               check=(png_size_t) ReadBlob(image,(size_t) length,
1477                 (char *) mng_info->read_buffer);
1478               mng_info->read_buffer[4]=0;
1479               mng_info->bytes_in_read_buffer=4;
1480               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1481                 mng_info->found_empty_plte=MagickTrue;
1482               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1483                 {
1484                   mng_info->found_empty_plte=MagickFalse;
1485                   mng_info->have_saved_bkgd_index=MagickFalse;
1486                 }
1487             }
1488 
1489           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1490               (data[3] == 1))
1491             {
1492               check=(png_size_t) ReadBlob(image,(size_t) length,
1493                 (char *) mng_info->read_buffer);
1494               mng_info->read_buffer[4]=0;
1495               mng_info->bytes_in_read_buffer=4;
1496               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1497                 if (mng_info->found_empty_plte)
1498                   {
1499                     /*
1500                       Skip the bKGD data byte and CRC.
1501                     */
1502                     check=(png_size_t)
1503                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1504                     check=(png_size_t) ReadBlob(image,(size_t) length,
1505                       (char *) mng_info->read_buffer);
1506                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1507                     mng_info->have_saved_bkgd_index=MagickTrue;
1508                     mng_info->bytes_in_read_buffer=0;
1509                   }
1510             }
1511         }
1512     }
1513 }
1514 #endif
1515 
png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)1516 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1517 {
1518   Image
1519     *image;
1520 
1521   image=(Image *) png_get_io_ptr(png_ptr);
1522   if (length != 0)
1523     {
1524       png_size_t
1525         check;
1526 
1527       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1528 
1529       if (check != length)
1530         png_error(png_ptr,"WriteBlob Failed");
1531     }
1532 }
1533 
png_flush_data(png_structp png_ptr)1534 static void png_flush_data(png_structp png_ptr)
1535 {
1536   (void) png_ptr;
1537 }
1538 
1539 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
PalettesAreEqual(Image * a,Image * b)1540 static int PalettesAreEqual(Image *a,Image *b)
1541 {
1542   ssize_t
1543     i;
1544 
1545   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1546     return((int) MagickFalse);
1547 
1548   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1549     return((int) MagickFalse);
1550 
1551   if (a->colors != b->colors)
1552     return((int) MagickFalse);
1553 
1554   for (i=0; i < (ssize_t) a->colors; i++)
1555   {
1556     if ((a->colormap[i].red != b->colormap[i].red) ||
1557         (a->colormap[i].green != b->colormap[i].green) ||
1558         (a->colormap[i].blue != b->colormap[i].blue))
1559       return((int) MagickFalse);
1560   }
1561 
1562   return((int) MagickTrue);
1563 }
1564 #endif
1565 
MngInfoDiscardObject(MngInfo * mng_info,int i)1566 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1567 {
1568   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1569       mng_info->exists[i] && !mng_info->frozen[i])
1570     {
1571 #ifdef MNG_OBJECT_BUFFERS
1572       if (mng_info->ob[i] != (MngBuffer *) NULL)
1573         {
1574           if (mng_info->ob[i]->reference_count > 0)
1575             mng_info->ob[i]->reference_count--;
1576 
1577           if (mng_info->ob[i]->reference_count == 0)
1578             {
1579               if (mng_info->ob[i]->image != (Image *) NULL)
1580                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1581 
1582               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1583             }
1584         }
1585       mng_info->ob[i]=(MngBuffer *) NULL;
1586 #endif
1587       mng_info->exists[i]=MagickFalse;
1588       mng_info->invisible[i]=MagickFalse;
1589       mng_info->viewable[i]=MagickFalse;
1590       mng_info->frozen[i]=MagickFalse;
1591       mng_info->x_off[i]=0;
1592       mng_info->y_off[i]=0;
1593       mng_info->object_clip[i].left=0;
1594       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1595       mng_info->object_clip[i].top=0;
1596       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1597     }
1598 }
1599 
MngInfoFreeStruct(MngInfo * mng_info)1600 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1601 {
1602   ssize_t
1603     i;
1604 
1605   if (mng_info == (MngInfo *) NULL)
1606     return((MngInfo *) NULL);
1607 
1608   for (i=1; i < MNG_MAX_OBJECTS; i++)
1609     MngInfoDiscardObject(mng_info,i);
1610 
1611   mng_info->global_plte=(png_colorp)
1612     RelinquishMagickMemory(mng_info->global_plte);
1613 
1614   return((MngInfo *) RelinquishMagickMemory(mng_info));
1615 }
1616 
mng_minimum_box(MngBox box1,MngBox box2)1617 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1618 {
1619   MngBox
1620     box;
1621 
1622   box=box1;
1623   if (box.left < box2.left)
1624     box.left=box2.left;
1625 
1626   if (box.top < box2.top)
1627     box.top=box2.top;
1628 
1629   if (box.right > box2.right)
1630     box.right=box2.right;
1631 
1632   if (box.bottom > box2.bottom)
1633     box.bottom=box2.bottom;
1634 
1635   return box;
1636 }
1637 
mng_get_long(unsigned char * p)1638 static long mng_get_long(unsigned char *p)
1639 {
1640   return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1641     ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1642 }
1643 
mng_read_box(MngBox previous_box,char delta_type,unsigned char * p)1644 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1645   unsigned char *p)
1646 {
1647    MngBox
1648       box;
1649 
1650   /*
1651     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1652   */
1653   box.left=mng_get_long(p);
1654   box.right=mng_get_long(&p[4]);
1655   box.top=mng_get_long(&p[8]);
1656   box.bottom=mng_get_long(&p[12]);
1657   if (delta_type != 0)
1658     {
1659       box.left+=previous_box.left;
1660       box.right+=previous_box.right;
1661       box.top+=previous_box.top;
1662       box.bottom+=previous_box.bottom;
1663     }
1664 
1665   return(box);
1666 }
1667 
mng_read_pair(MngPair previous_pair,int delta_type,unsigned char * p)1668 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1669   unsigned char *p)
1670 {
1671   MngPair
1672     pair;
1673 
1674   /*
1675     Read two ssize_t's from CLON, MOVE or PAST chunk
1676   */
1677   pair.a=mng_get_long(p);
1678   pair.b=mng_get_long(&p[4]);
1679   if (delta_type != 0)
1680     {
1681       pair.a+=previous_pair.a;
1682       pair.b+=previous_pair.b;
1683     }
1684 
1685   return(pair);
1686 }
1687 
1688 typedef struct _PNGErrorInfo
1689 {
1690   Image
1691     *image;
1692 
1693   ExceptionInfo
1694     *exception;
1695 } PNGErrorInfo;
1696 
MagickPNGErrorHandler(png_struct * ping,png_const_charp message)1697 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1698 {
1699   ExceptionInfo
1700     *exception;
1701 
1702   Image
1703     *image;
1704 
1705   PNGErrorInfo
1706     *error_info;
1707 
1708   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1709   image=error_info->image;
1710   exception=error_info->exception;
1711 
1712   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1713     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1714 
1715   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1716     "`%s'",image->filename);
1717 
1718 #if (PNG_LIBPNG_VER < 10500)
1719   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1720    * are building with libpng-1.4.x and can be ignored.
1721    */
1722   longjmp(ping->jmpbuf,1);
1723 #else
1724   png_longjmp(ping,1);
1725 #endif
1726 }
1727 
MagickPNGWarningHandler(png_struct * ping,png_const_charp message)1728 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1729 {
1730   ExceptionInfo
1731     *exception;
1732 
1733   Image
1734     *image;
1735 
1736   PNGErrorInfo
1737     *error_info;
1738 
1739   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1740     png_error(ping, message);
1741 
1742   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1743   image=error_info->image;
1744   exception=error_info->exception;
1745   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1746     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1747 
1748   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1749     message,"`%s'",image->filename);
1750 }
1751 
1752 #ifdef PNG_USER_MEM_SUPPORTED
1753 #if PNG_LIBPNG_VER >= 10400
Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)1754 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1755 #else
1756 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1757 #endif
1758 {
1759   (void) png_ptr;
1760   return((png_voidp) AcquireQuantumMemory(1,(size_t) size));
1761 }
1762 
1763 /*
1764   Free a pointer.  It is removed from the list at the same time.
1765 */
Magick_png_free(png_structp png_ptr,png_voidp ptr)1766 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1767 {
1768   (void) png_ptr;
1769   ptr=RelinquishMagickMemory(ptr);
1770   return((png_free_ptr) NULL);
1771 }
1772 #endif
1773 
1774 #if defined(__cplusplus) || defined(c_plusplus)
1775 }
1776 #endif
1777 
1778 static int
Magick_png_read_raw_profile(png_struct * ping,Image * image,const ImageInfo * image_info,png_textp text,int ii,ExceptionInfo * exception)1779 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1780    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1781 {
1782   png_charp
1783     ep;
1784 
1785   ssize_t
1786     i;
1787 
1788   unsigned char
1789     *dp;
1790 
1791   png_charp
1792     sp;
1793 
1794   size_t
1795     extent,
1796     nibbles;
1797 
1798   ssize_t
1799     length;
1800 
1801   StringInfo
1802     *profile;
1803 
1804   static const unsigned char
1805     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1806                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1807                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1808                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1809                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1810                  13,14,15};
1811 
1812   sp=text[ii].text+1;
1813   extent=text[ii].text_length;
1814   ep=text[ii].text+extent;
1815   if (ep <= sp)
1816     {
1817       png_warning(ping,"invalid profile length");
1818       return(MagickFalse);
1819     }
1820   /* look for newline */
1821   while ((*sp != '\n') && extent--)
1822     sp++;
1823 
1824   /* look for length */
1825   while (((*sp == '\0' || *sp == ' ' || *sp == '\n')) && extent--)
1826      sp++;
1827 
1828   if (extent == 0)
1829     {
1830       png_warning(ping,"invalid profile length");
1831       return(MagickFalse);
1832     }
1833 
1834   length=StringToLong(sp);
1835 
1836   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1837        "      length: %lu",(unsigned long) length);
1838 
1839   while ((*sp != ' ' && *sp != '\n') && extent--)
1840     sp++;
1841 
1842   if (extent == 0)
1843     {
1844       png_warning(ping,"missing profile length");
1845       return(MagickFalse);
1846     }
1847 
1848   /* allocate space */
1849   if (length == 0)
1850   {
1851     png_warning(ping,"invalid profile length");
1852     return(MagickFalse);
1853   }
1854 
1855   profile=BlobToStringInfo((const void *) NULL,length);
1856 
1857   if (profile == (StringInfo *) NULL)
1858   {
1859     png_warning(ping, "unable to copy profile");
1860     return(MagickFalse);
1861   }
1862 
1863   /* copy profile, skipping white space and column 1 "=" signs */
1864   dp=GetStringInfoDatum(profile);
1865   nibbles=length*2;
1866 
1867   for (i=0; i < (ssize_t) nibbles; i++)
1868   {
1869     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1870     {
1871       if (*sp == '\0')
1872         {
1873           png_warning(ping, "ran out of profile data");
1874           profile=DestroyStringInfo(profile);
1875           return(MagickFalse);
1876         }
1877       sp++;
1878     }
1879 
1880     if (i%2 == 0)
1881       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1882 
1883     else
1884       (*dp++)+=unhex[(int) *sp++];
1885   }
1886   /*
1887     We have already read "Raw profile type.
1888   */
1889   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1890   profile=DestroyStringInfo(profile);
1891 
1892   if (image_info->verbose)
1893     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1894 
1895   return MagickTrue;
1896 }
1897 
PNGSetExifProfile(Image * image,png_size_t size,png_byte * data,ExceptionInfo * exception)1898 static int PNGSetExifProfile(Image *image,png_size_t size,png_byte *data,
1899   ExceptionInfo *exception)
1900 {
1901   StringInfo
1902     *profile;
1903 
1904   unsigned char
1905     *p;
1906 
1907   png_byte
1908     *s;
1909 
1910   size_t
1911     i;
1912 
1913   profile=BlobToStringInfo((const void *) NULL,size+6);
1914 
1915   if (profile == (StringInfo *) NULL)
1916     {
1917       (void) ThrowMagickException(exception,GetMagickModule(),
1918         ResourceLimitError,"MemoryAllocationFailed","`%s'",
1919         image->filename);
1920       return(-1);
1921     }
1922   p=GetStringInfoDatum(profile);
1923 
1924   /* Initialize profile with "Exif\0\0" */
1925   *p++ ='E';
1926   *p++ ='x';
1927   *p++ ='i';
1928   *p++ ='f';
1929   *p++ ='\0';
1930   *p++ ='\0';
1931 
1932   s=data;
1933   i=0;
1934   if (size > 6)
1935     {
1936       /* Skip first 6 bytes if "Exif\0\0" is
1937           already present by accident
1938       */
1939       if (s[0] == 'E' && s[1] == 'x'  && s[2] == 'i' &&
1940           s[3] == 'f' && s[4] == '\0' && s[5] == '\0')
1941       {
1942         s+=6;
1943         i=6;
1944         SetStringInfoLength(profile,size);
1945         p=GetStringInfoDatum(profile);
1946       }
1947     }
1948 
1949   /* copy chunk->data to profile */
1950   for (; i<size; i++)
1951     *p++ = *s++;
1952 
1953   (void) SetImageProfile(image,"exif",profile,exception);
1954 
1955   profile=DestroyStringInfo(profile);
1956 
1957   return(1);
1958 }
1959 
1960 #if defined(PNG_READ_eXIf_SUPPORTED)
read_eXIf_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)1961 static void read_eXIf_chunk(Image *image,png_struct *ping,png_info *info,
1962   ExceptionInfo *exception)
1963 {
1964   png_uint_32
1965     size;
1966 
1967   png_bytep
1968     data;
1969 
1970 #if PNG_LIBPNG_VER > 10631
1971   if (png_get_eXIf_1(ping,info,&size,&data))
1972     (void) PNGSetExifProfile(image,size,data,exception);
1973 #endif
1974 }
1975 #endif
1976 
1977 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1978 
read_user_chunk_callback(png_struct * ping,png_unknown_chunkp chunk)1979 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1980 {
1981   Image
1982     *image;
1983 
1984   PNGErrorInfo
1985     *error_info;
1986 
1987   /* The unknown chunk structure contains the chunk data:
1988      png_byte name[5];
1989      png_byte *data;
1990      png_size_t size;
1991 
1992      Note that libpng has already taken care of the CRC handling.
1993 
1994      Returns one of the following:
1995          return(-n);  chunk had an error
1996          return(0);  did not recognize
1997          return(n);  success
1998   */
1999 
2000   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2001      "    read_user_chunk: found %c%c%c%c chunk",
2002        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
2003 
2004   if (chunk->name[0]  == 101 &&
2005       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
2006       chunk->name[2] ==   73 &&
2007       chunk-> name[3] == 102)
2008     {
2009       /* process eXIf or exIf chunk */
2010 
2011       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2012         " recognized eXIf chunk");
2013 
2014       image=(Image *) png_get_user_chunk_ptr(ping);
2015 
2016       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2017 
2018       return(PNGSetExifProfile(image,chunk->size,chunk->data,
2019         error_info->exception));
2020     }
2021 
2022   /* orNT */
2023   if (chunk->name[0] == 111 &&
2024       chunk->name[1] == 114 &&
2025       chunk->name[2] ==  78 &&
2026       chunk->name[3] ==  84)
2027     {
2028      /* recognized orNT */
2029      if (chunk->size != 1)
2030        return(-1); /* Error return */
2031 
2032      image=(Image *) png_get_user_chunk_ptr(ping);
2033 
2034      image->orientation=
2035        Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
2036 
2037      return(1);
2038     }
2039 
2040   /* vpAg (deprecated, replaced by caNv) */
2041   if (chunk->name[0] == 118 &&
2042       chunk->name[1] == 112 &&
2043       chunk->name[2] ==  65 &&
2044       chunk->name[3] == 103)
2045     {
2046       /* recognized vpAg */
2047 
2048       if (chunk->size != 9)
2049         return(-1); /* Error return */
2050 
2051       if (chunk->data[8] != 0)
2052         return(0);  /* ImageMagick requires pixel units */
2053 
2054       image=(Image *) png_get_user_chunk_ptr(ping);
2055 
2056       image->page.width=(size_t)mng_get_long(chunk->data);
2057       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2058 
2059       return(1);
2060     }
2061 
2062   /* caNv */
2063   if (chunk->name[0] ==  99 &&
2064       chunk->name[1] ==  97 &&
2065       chunk->name[2] ==  78 &&
2066       chunk->name[3] == 118)
2067     {
2068       /* recognized caNv */
2069 
2070       if (chunk->size != 16)
2071         return(-1); /* Error return */
2072 
2073       image=(Image *) png_get_user_chunk_ptr(ping);
2074 
2075       image->page.width=(size_t) mng_get_long(chunk->data);
2076       image->page.height=(size_t) mng_get_long(&chunk->data[4]);
2077       image->page.x=(ssize_t) ((int) mng_get_long(&chunk->data[8]));
2078       image->page.y=(ssize_t) ((int) mng_get_long(&chunk->data[12]));
2079 
2080       return(1);
2081     }
2082 
2083   /* acTL */
2084   if ((chunk->name[0]  == 97) && (chunk->name[1]  == 99) &&
2085       (chunk->name[2]  == 84) && (chunk->name[3]  == 76))
2086     {
2087       image=(Image *) png_get_user_chunk_ptr(ping);
2088       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
2089 
2090       (void) SetImageProperty(image,"png:acTL","chunk was found",
2091         error_info->exception);
2092 
2093       return(1);
2094     }
2095 
2096   return(0); /* Did not recognize */
2097 }
2098 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2099 
2100 #if defined(PNG_tIME_SUPPORTED)
read_tIME_chunk(Image * image,png_struct * ping,png_info * info,ExceptionInfo * exception)2101 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2102   ExceptionInfo *exception)
2103 {
2104   png_timep
2105     time;
2106 
2107   if (png_get_tIME(ping,info,&time))
2108     {
2109       char
2110         timestamp[21];
2111 
2112       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2113         time->year,time->month,time->day,time->hour,time->minute,time->second);
2114       SetImageProperty(image,"png:tIME",timestamp,exception);
2115     }
2116 }
2117 #endif
2118 
2119 /*
2120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2121 %                                                                             %
2122 %                                                                             %
2123 %                                                                             %
2124 %   R e a d O n e P N G I m a g e                                             %
2125 %                                                                             %
2126 %                                                                             %
2127 %                                                                             %
2128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2129 %
2130 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2131 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2132 %  necessary for the new Image structure and returns a pointer to the new
2133 %  image.
2134 %
2135 %  The format of the ReadOnePNGImage method is:
2136 %
2137 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2138 %         ExceptionInfo *exception)
2139 %
2140 %  A description of each parameter follows:
2141 %
2142 %    o mng_info: Specifies a pointer to a MngInfo structure.
2143 %
2144 %    o image_info: the image info.
2145 %
2146 %    o exception: return any errors or warnings in this structure.
2147 %
2148 */
ReadOnePNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)2149 static Image *ReadOnePNGImage(MngInfo *mng_info,
2150     const ImageInfo *image_info, ExceptionInfo *exception)
2151 {
2152   /* Read one PNG image */
2153 
2154   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2155 
2156   Image
2157     *image;
2158 
2159   int
2160     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2161     num_raw_profiles,
2162     num_text,
2163     num_text_total,
2164     num_passes,
2165     number_colors,
2166     pass,
2167     ping_bit_depth,
2168     ping_color_type,
2169     ping_file_depth,
2170     ping_interlace_method,
2171     ping_compression_method,
2172     ping_filter_method,
2173     ping_num_trans,
2174     unit_type;
2175 
2176   double
2177     file_gamma;
2178 
2179   MagickBooleanType
2180     logging,
2181     ping_found_cHRM,
2182     ping_found_gAMA,
2183     ping_found_iCCP,
2184     ping_found_sRGB,
2185     ping_found_sRGB_cHRM,
2186     ping_preserve_iCCP,
2187     status;
2188 
2189   MemoryInfo
2190     *volatile pixel_info;
2191 
2192   PixelInfo
2193     transparent_color;
2194 
2195   PNGErrorInfo
2196     error_info;
2197 
2198   png_bytep
2199      ping_trans_alpha = NULL;
2200 
2201   png_color_16p
2202      ping_background = (png_color_16p) NULL,
2203      ping_trans_color = (png_color_16p) NULL;
2204 
2205   png_info
2206     *end_info,
2207     *ping_info;
2208 
2209   png_struct
2210     *ping;
2211 
2212   png_textp
2213     text;
2214 
2215   png_uint_32
2216     ping_height,
2217     ping_width,
2218     x_resolution,
2219     y_resolution;
2220 
2221   QuantumInfo
2222     *volatile quantum_info;
2223 
2224   Quantum
2225     *volatile quantum_scanline;
2226 
2227   ssize_t
2228     y;
2229 
2230   unsigned char
2231     *p;
2232 
2233   ssize_t
2234     i,
2235     x;
2236 
2237   Quantum
2238     *q;
2239 
2240   size_t
2241     length,
2242     ping_rowbytes,
2243     row_offset;
2244 
2245   ssize_t
2246     j;
2247 
2248   unsigned char
2249     *ping_pixels;
2250 
2251 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2252   png_byte unused_chunks[]=
2253   {
2254     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2255     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2256     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2257     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2258     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2259 #if !defined(PNG_tIME_SUPPORTED)
2260     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2261 #endif
2262 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2263                           /* ignore the APNG chunks */
2264      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2265     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2266     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2267 #endif
2268   };
2269 #endif
2270 
2271   logging=IsEventLogging();
2272   if (logging != MagickFalse)
2273     {
2274       char
2275         im_vers[32],
2276         libpng_runv[32],
2277         libpng_vers[32],
2278         zlib_runv[32],
2279         zlib_vers[32];
2280 
2281       *im_vers='\0';
2282       (void) ConcatenateMagickString(im_vers,
2283              MagickLibVersionText,32);
2284       (void) ConcatenateMagickString(im_vers,
2285              MagickLibAddendum,32);
2286 
2287       *libpng_vers='\0';
2288       (void) ConcatenateMagickString(libpng_vers,
2289              PNG_LIBPNG_VER_STRING,32);
2290       *libpng_runv='\0';
2291       (void) ConcatenateMagickString(libpng_runv,
2292              png_get_libpng_ver(NULL),32);
2293 
2294       *zlib_vers='\0';
2295       (void) ConcatenateMagickString(zlib_vers,
2296              ZLIB_VERSION,32);
2297       *zlib_runv='\0';
2298       (void) ConcatenateMagickString(zlib_runv,
2299              zlib_version,32);
2300 
2301       LogMagickEvent(CoderEvent,GetMagickModule(),
2302          "  Enter ReadOnePNGImage()\n"
2303          "    IM version     = %s\n"
2304          "    Libpng version = %s",
2305          im_vers, libpng_vers);
2306 
2307       if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2308         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2309           "      running with   %s", libpng_runv);
2310       if (LocaleCompare(libpng_vers,zlib_vers) != 0)
2311         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2312           "    Zlib version   = %s", zlib_vers);
2313       if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2314         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2315           "      running with   %s", zlib_runv);
2316     }
2317 
2318 #if (PNG_LIBPNG_VER < 10200)
2319   if (image_info->verbose)
2320     printf("Your PNG library (libpng-%s) is rather old.\n",
2321        PNG_LIBPNG_VER_STRING);
2322 #endif
2323 
2324 #if (PNG_LIBPNG_VER >= 10400)
2325 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2326   if (image_info->verbose)
2327     {
2328       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2329            PNG_LIBPNG_VER_STRING);
2330       printf("Please update it.\n");
2331     }
2332 #  endif
2333 #endif
2334 
2335 
2336   quantum_info = (QuantumInfo *) NULL;
2337   image=mng_info->image;
2338 
2339   if (logging != MagickFalse)
2340   {
2341     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2342        "    Before reading:\n"
2343        "      image->alpha_trait=%d\n"
2344        "      image->rendering_intent=%d\n"
2345        "      image->colorspace=%d\n"
2346        "      image->gamma=%f",
2347        (int) image->alpha_trait, (int) image->rendering_intent,
2348        (int) image->colorspace, image->gamma);
2349   }
2350   intent=
2351     Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2352 
2353   /* Set to an out-of-range color unless tRNS chunk is present */
2354   transparent_color.red=65537;
2355   transparent_color.green=65537;
2356   transparent_color.blue=65537;
2357   transparent_color.alpha=65537;
2358 
2359   number_colors=0;
2360   num_text = 0;
2361   num_text_total = 0;
2362   num_raw_profiles = 0;
2363 
2364   ping_found_cHRM = MagickFalse;
2365   ping_found_gAMA = MagickFalse;
2366   ping_found_iCCP = MagickFalse;
2367   ping_found_sRGB = MagickFalse;
2368   ping_found_sRGB_cHRM = MagickFalse;
2369   ping_preserve_iCCP = MagickFalse;
2370 
2371 
2372   /*
2373     Allocate the PNG structures
2374   */
2375 #ifdef PNG_USER_MEM_SUPPORTED
2376  error_info.image=image;
2377  error_info.exception=exception;
2378  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2379    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2380    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2381 #else
2382   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2383     MagickPNGErrorHandler,MagickPNGWarningHandler);
2384 #endif
2385   if (ping == (png_struct *) NULL)
2386     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2387 
2388   ping_info=png_create_info_struct(ping);
2389 
2390   if (ping_info == (png_info *) NULL)
2391     {
2392       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2393       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2394     }
2395 
2396   end_info=png_create_info_struct(ping);
2397 
2398   if (end_info == (png_info *) NULL)
2399     {
2400       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2401       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2402     }
2403 
2404   pixel_info=(MemoryInfo *) NULL;
2405   quantum_scanline = (Quantum *) NULL;
2406   quantum_info = (QuantumInfo *) NULL;
2407 
2408   if (setjmp(png_jmpbuf(ping)))
2409     {
2410       /*
2411         PNG image is corrupt.
2412       */
2413       png_destroy_read_struct(&ping,&ping_info,&end_info);
2414 
2415       if (pixel_info != (MemoryInfo *) NULL)
2416         pixel_info=RelinquishVirtualMemory(pixel_info);
2417 
2418       if (quantum_info != (QuantumInfo *) NULL)
2419         quantum_info=DestroyQuantumInfo(quantum_info);
2420 
2421       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2422 
2423 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2424       UnlockSemaphoreInfo(ping_semaphore);
2425 #endif
2426 
2427       if (logging != MagickFalse)
2428         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2429           "  exit ReadOnePNGImage() with error.");
2430 
2431       if (image != (Image *) NULL)
2432         image=DestroyImageList(image);
2433       return(image);
2434     }
2435 
2436   /* {  For navigation to end of SETJMP-protected block.  Within this
2437    *    block, use png_error() instead of Throwing an Exception, to ensure
2438    *    that libpng is able to clean up, and that the semaphore is unlocked.
2439    */
2440 
2441 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2442   LockSemaphoreInfo(ping_semaphore);
2443 #endif
2444 
2445 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2446   /* Allow benign errors */
2447   png_set_benign_errors(ping, 1);
2448 #endif
2449 
2450 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2451   {
2452     const char
2453       *option;
2454 
2455     /* Reject images with too many rows or columns */
2456     png_set_user_limits(ping,(png_uint_32) MagickMin(PNG_UINT_31_MAX,
2457       GetMagickResourceLimit(WidthResource)),(png_uint_32)
2458       MagickMin(PNG_UINT_31_MAX,GetMagickResourceLimit(HeightResource)));
2459 
2460 #if (PNG_LIBPNG_VER >= 10400)
2461     option=GetImageOption(image_info,"png:chunk-cache-max");
2462     if (option != (const char *) NULL)
2463       png_set_chunk_cache_max(ping,(png_uint_32) MagickMin(PNG_UINT_32_MAX,
2464         StringToLong(option)));
2465     else
2466       png_set_chunk_cache_max(ping,32767);
2467 #endif
2468 
2469 #if (PNG_LIBPNG_VER >= 10401)
2470     option=GetImageOption(image_info,"png:chunk-malloc-max");
2471     if (option != (const char *) NULL)
2472       png_set_chunk_malloc_max(ping,(png_alloc_size_t) MagickMin(PNG_SIZE_MAX,
2473         (size_t) StringToLong(option)));
2474     else
2475       png_set_chunk_malloc_max(ping,(png_alloc_size_t) GetMaxMemoryRequest());
2476 #endif
2477   }
2478 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2479 
2480   /*
2481     Prepare PNG for reading.
2482   */
2483 
2484   mng_info->image_found++;
2485   png_set_sig_bytes(ping,8);
2486 
2487   if (LocaleCompare(image_info->magick,"MNG") == 0)
2488     {
2489 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2490       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2491       png_set_read_fn(ping,image,png_get_data);
2492 #else
2493 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2494       png_permit_empty_plte(ping,MagickTrue);
2495       png_set_read_fn(ping,image,png_get_data);
2496 #else
2497       mng_info->image=image;
2498       mng_info->bytes_in_read_buffer=0;
2499       mng_info->found_empty_plte=MagickFalse;
2500       mng_info->have_saved_bkgd_index=MagickFalse;
2501       png_set_read_fn(ping,mng_info,mng_get_data);
2502 #endif
2503 #endif
2504     }
2505 
2506   else
2507     png_set_read_fn(ping,image,png_get_data);
2508 
2509   {
2510     const char
2511       *value;
2512 
2513     value=GetImageOption(image_info,"png:ignore-crc");
2514     if (IsStringTrue(value) != MagickFalse)
2515     {
2516        /* Turn off CRC checking while reading */
2517        png_set_crc_action(ping, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
2518 #ifdef PNG_IGNORE_ADLER32
2519        /* Turn off ADLER32 checking while reading */
2520        png_set_option(ping, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
2521 #endif
2522      }
2523 
2524     value=GetImageOption(image_info,"profile:skip");
2525 
2526     if (IsOptionMember("ICC",value) == MagickFalse)
2527     {
2528 
2529        value=GetImageOption(image_info,"png:preserve-iCCP");
2530 
2531        if (value == NULL)
2532           value=GetImageArtifact(image,"png:preserve-iCCP");
2533 
2534        if (value != NULL)
2535           ping_preserve_iCCP=MagickTrue;
2536 
2537 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2538        /* Don't let libpng check for ICC/sRGB profile because we're going
2539         * to do that anyway.  This feature was added at libpng-1.6.12.
2540         * If logging, go ahead and check and issue a warning as appropriate.
2541         */
2542        if (logging == MagickFalse)
2543           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2544 #endif
2545     }
2546 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2547     else
2548     {
2549        png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2550     }
2551 #endif
2552   }
2553 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2554   /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2555 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2556   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2557 # else
2558   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2559 # endif
2560   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2561   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2562   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2563      (int)sizeof(unused_chunks)/5);
2564   /* Callback for other unknown chunks */
2565   png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2566 #endif
2567 
2568 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2569     /* Disable new libpng-1.5.10 feature */
2570     png_set_check_for_invalid_index (ping, 0);
2571 #endif
2572 
2573 #if (PNG_LIBPNG_VER < 10400)
2574 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2575    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2576   /* Disable thread-unsafe features of pnggccrd */
2577   if (png_access_version_number() >= 10200)
2578   {
2579     png_uint_32 mmx_disable_mask=0;
2580     png_uint_32 asm_flags;
2581 
2582     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2583                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2584                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2585                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2586     asm_flags=png_get_asm_flags(ping);
2587     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2588   }
2589 #  endif
2590 #endif
2591 
2592   png_read_info(ping,ping_info);
2593 
2594   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2595                &ping_bit_depth,&ping_color_type,
2596                &ping_interlace_method,&ping_compression_method,
2597                &ping_filter_method);
2598 
2599   ping_file_depth = ping_bit_depth;
2600 
2601   /* Swap bytes if requested */
2602   if (ping_file_depth == 16)
2603   {
2604      const char
2605        *value;
2606 
2607      value=GetImageOption(image_info,"png:swap-bytes");
2608 
2609      if (value == NULL)
2610         value=GetImageArtifact(image,"png:swap-bytes");
2611 
2612      if (value != NULL)
2613         png_set_swap(ping);
2614   }
2615 
2616   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2617   {
2618       char
2619         msg[MagickPathExtent];
2620 
2621       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2622          (int) ping_color_type);
2623       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2624 
2625       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2626          (int) ping_bit_depth);
2627       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2628   }
2629 
2630   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2631                       &ping_trans_color);
2632 
2633   (void) png_get_bKGD(ping, ping_info, &ping_background);
2634 
2635   if (ping_bit_depth < 8)
2636     {
2637        png_set_packing(ping);
2638        ping_bit_depth = 8;
2639     }
2640 
2641   image->depth=ping_bit_depth;
2642   image->depth=GetImageQuantumDepth(image,MagickFalse);
2643   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2644 
2645   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2646       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2647     {
2648       image->rendering_intent=UndefinedIntent;
2649       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2650       (void) memset(&image->chromaticity,0,
2651         sizeof(image->chromaticity));
2652     }
2653 
2654   if (logging != MagickFalse)
2655     {
2656       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657         "    PNG width: %.20g, height: %.20g\n"
2658         "    PNG color_type: %d, bit_depth: %d\n"
2659         "    PNG compression_method: %d\n"
2660         "    PNG interlace_method: %d, filter_method: %d",
2661         (double) ping_width, (double) ping_height,
2662         ping_color_type, ping_bit_depth,
2663         ping_compression_method,
2664         ping_interlace_method,ping_filter_method);
2665 
2666     }
2667 
2668   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2669     {
2670       ping_found_iCCP=MagickTrue;
2671       if (logging != MagickFalse)
2672         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2673           "    Found PNG iCCP chunk.");
2674     }
2675 
2676   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2677     {
2678       ping_found_gAMA=MagickTrue;
2679       if (logging != MagickFalse)
2680         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2681           "    Found PNG gAMA chunk.");
2682     }
2683 
2684   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2685     {
2686       ping_found_cHRM=MagickTrue;
2687       if (logging != MagickFalse)
2688         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2689           "    Found PNG cHRM chunk.");
2690     }
2691 
2692   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2693       PNG_INFO_sRGB))
2694     {
2695       ping_found_sRGB=MagickTrue;
2696       if (logging != MagickFalse)
2697         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2698           "    Found PNG sRGB chunk.");
2699     }
2700 
2701 #ifdef PNG_READ_iCCP_SUPPORTED
2702     if (ping_found_iCCP !=MagickTrue &&
2703       ping_found_sRGB != MagickTrue &&
2704       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2705     {
2706       ping_found_iCCP=MagickTrue;
2707       if (logging != MagickFalse)
2708         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2709           "    Found PNG iCCP chunk.");
2710     }
2711 
2712   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2713     {
2714       int
2715         compression;
2716 
2717 #if (PNG_LIBPNG_VER < 10500)
2718       png_charp
2719         info;
2720 #else
2721       png_bytep
2722         info;
2723 #endif
2724 
2725       png_charp
2726         name;
2727 
2728       png_uint_32
2729         profile_length;
2730 
2731       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2732         &profile_length);
2733 
2734       if (profile_length != 0)
2735         {
2736           StringInfo
2737             *profile;
2738 
2739           if (logging != MagickFalse)
2740             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2741               "    Reading PNG iCCP chunk.");
2742 
2743           profile=BlobToStringInfo(info,(const size_t) profile_length);
2744 
2745           if (profile == (StringInfo *) NULL)
2746           {
2747             png_warning(ping, "ICC profile is NULL");
2748             profile=DestroyStringInfo(profile);
2749           }
2750           else
2751           {
2752             if (ping_preserve_iCCP == MagickFalse)
2753             {
2754                  int
2755                    icheck,
2756                    got_crc=0;
2757 
2758 
2759                  png_uint_32
2760                    profile_crc=0;
2761 
2762                  unsigned char
2763                    *data;
2764 
2765                  profile_length=(png_uint_32) GetStringInfoLength(profile);
2766 
2767                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2768                  {
2769                    if (profile_length == sRGB_info[icheck].len)
2770                    {
2771                      if (got_crc == 0)
2772                      {
2773                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2774                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2775                          (unsigned long) profile_length);
2776 
2777                        data=GetStringInfoDatum(profile);
2778                        profile_crc=crc32(0,data,profile_length);
2779 
2780                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2781                            "      with crc=%8x",(unsigned int) profile_crc);
2782                        got_crc++;
2783                      }
2784 
2785                      if (profile_crc == sRGB_info[icheck].crc)
2786                      {
2787                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2788                             "      It is sRGB with rendering intent = %s",
2789                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2790                              sRGB_info[icheck].intent));
2791                         if (image->rendering_intent==UndefinedIntent)
2792                         {
2793                           image->rendering_intent=
2794                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2795                              sRGB_info[icheck].intent);
2796                         }
2797                         break;
2798                      }
2799                    }
2800                  }
2801                  if (sRGB_info[icheck].len == 0)
2802                  {
2803                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2804                         "    Got %lu-byte ICC profile not recognized as sRGB",
2805                         (unsigned long) profile_length);
2806                     (void) SetImageProfile(image,"icc",profile,exception);
2807                  }
2808             }
2809             else /* Preserve-iCCP */
2810             {
2811                     (void) SetImageProfile(image,"icc",profile,exception);
2812             }
2813 
2814             profile=DestroyStringInfo(profile);
2815           }
2816       }
2817     }
2818 #endif
2819 
2820 #if defined(PNG_READ_sRGB_SUPPORTED)
2821   {
2822     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2823         PNG_INFO_sRGB))
2824     {
2825       if (png_get_sRGB(ping,ping_info,&intent))
2826       {
2827         if (image->rendering_intent == UndefinedIntent)
2828           image->rendering_intent=
2829              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2830 
2831         if (logging != MagickFalse)
2832           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2833             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2834       }
2835     }
2836 
2837     else if (mng_info->have_global_srgb)
2838       {
2839         if (image->rendering_intent == UndefinedIntent)
2840           image->rendering_intent=
2841             Magick_RenderingIntent_from_PNG_RenderingIntent
2842             (mng_info->global_srgb_intent);
2843       }
2844   }
2845 #endif
2846 
2847 
2848   {
2849      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2850        if (mng_info->have_global_gama)
2851          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2852 
2853      if (png_get_gAMA(ping,ping_info,&file_gamma))
2854        {
2855          image->gamma=file_gamma;
2856          if (logging != MagickFalse)
2857            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2858              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2859        }
2860   }
2861 
2862   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2863     {
2864       if (mng_info->have_global_chrm != MagickFalse)
2865         {
2866           (void) png_set_cHRM(ping,ping_info,
2867             mng_info->global_chrm.white_point.x,
2868             mng_info->global_chrm.white_point.y,
2869             mng_info->global_chrm.red_primary.x,
2870             mng_info->global_chrm.red_primary.y,
2871             mng_info->global_chrm.green_primary.x,
2872             mng_info->global_chrm.green_primary.y,
2873             mng_info->global_chrm.blue_primary.x,
2874             mng_info->global_chrm.blue_primary.y);
2875         }
2876     }
2877 
2878   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2879     {
2880       (void) png_get_cHRM(ping,ping_info,
2881         &image->chromaticity.white_point.x,
2882         &image->chromaticity.white_point.y,
2883         &image->chromaticity.red_primary.x,
2884         &image->chromaticity.red_primary.y,
2885         &image->chromaticity.green_primary.x,
2886         &image->chromaticity.green_primary.y,
2887         &image->chromaticity.blue_primary.x,
2888         &image->chromaticity.blue_primary.y);
2889 
2890        ping_found_cHRM=MagickTrue;
2891 
2892        if (image->chromaticity.red_primary.x>0.6399f &&
2893            image->chromaticity.red_primary.x<0.6401f &&
2894            image->chromaticity.red_primary.y>0.3299f &&
2895            image->chromaticity.red_primary.y<0.3301f &&
2896            image->chromaticity.green_primary.x>0.2999f &&
2897            image->chromaticity.green_primary.x<0.3001f &&
2898            image->chromaticity.green_primary.y>0.5999f &&
2899            image->chromaticity.green_primary.y<0.6001f &&
2900            image->chromaticity.blue_primary.x>0.1499f &&
2901            image->chromaticity.blue_primary.x<0.1501f &&
2902            image->chromaticity.blue_primary.y>0.0599f &&
2903            image->chromaticity.blue_primary.y<0.0601f &&
2904            image->chromaticity.white_point.x>0.3126f &&
2905            image->chromaticity.white_point.x<0.3128f &&
2906            image->chromaticity.white_point.y>0.3289f &&
2907            image->chromaticity.white_point.y<0.3291f)
2908           ping_found_sRGB_cHRM=MagickTrue;
2909     }
2910 
2911   if (image->rendering_intent != UndefinedIntent)
2912     {
2913       if (ping_found_sRGB != MagickTrue &&
2914           (ping_found_gAMA != MagickTrue ||
2915           (image->gamma > .45 && image->gamma < .46)) &&
2916           (ping_found_cHRM != MagickTrue ||
2917           ping_found_sRGB_cHRM != MagickFalse) &&
2918           ping_found_iCCP != MagickTrue)
2919       {
2920          png_set_sRGB(ping,ping_info,
2921             Magick_RenderingIntent_to_PNG_RenderingIntent
2922             (image->rendering_intent));
2923          file_gamma=0.45455f;
2924          ping_found_sRGB=MagickTrue;
2925          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2926            "    Setting sRGB as if in input");
2927       }
2928     }
2929 
2930 #if defined(PNG_oFFs_SUPPORTED)
2931   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2932     {
2933       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2934       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2935 
2936       if (logging != MagickFalse)
2937         if (image->page.x || image->page.y)
2938           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2939             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2940             image->page.x,(double) image->page.y);
2941     }
2942 #endif
2943 #if defined(PNG_pHYs_SUPPORTED)
2944   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2945     {
2946       if (mng_info->have_global_phys)
2947         {
2948           png_set_pHYs(ping,ping_info,
2949                        mng_info->global_x_pixels_per_unit,
2950                        mng_info->global_y_pixels_per_unit,
2951                        mng_info->global_phys_unit_type);
2952         }
2953     }
2954 
2955   x_resolution=0;
2956   y_resolution=0;
2957   unit_type=0;
2958   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2959     {
2960       /*
2961         Set image resolution.
2962       */
2963       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2964         &unit_type);
2965       image->resolution.x=(double) x_resolution;
2966       image->resolution.y=(double) y_resolution;
2967 
2968       if (unit_type == PNG_RESOLUTION_METER)
2969         {
2970           image->units=PixelsPerCentimeterResolution;
2971           image->resolution.x=(double) x_resolution/100.0;
2972           image->resolution.y=(double) y_resolution/100.0;
2973         }
2974 
2975       if (logging != MagickFalse)
2976         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2977           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2978           (double) x_resolution,(double) y_resolution,unit_type);
2979     }
2980 #endif
2981 
2982   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2983     {
2984       png_colorp
2985         palette;
2986 
2987       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2988 
2989       if ((number_colors == 0) &&
2990           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2991         {
2992           if (mng_info->global_plte_length)
2993             {
2994               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2995                 (int) mng_info->global_plte_length);
2996 
2997               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2998               {
2999                 if (mng_info->global_trns_length)
3000                   {
3001                     png_warning(ping,
3002                       "global tRNS has more entries than global PLTE");
3003                   }
3004                 else
3005                   {
3006                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
3007                        (int) mng_info->global_trns_length,NULL);
3008                   }
3009                }
3010 #ifdef PNG_READ_bKGD_SUPPORTED
3011               if (
3012 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3013                    mng_info->have_saved_bkgd_index ||
3014 #endif
3015                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3016                     {
3017                       png_color_16
3018                          background;
3019 
3020 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
3021                       if (mng_info->have_saved_bkgd_index)
3022                         background.index=mng_info->saved_bkgd_index;
3023 #endif
3024                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
3025                         background.index=ping_background->index;
3026 
3027                       background.red=(png_uint_16)
3028                         mng_info->global_plte[background.index].red;
3029 
3030                       background.green=(png_uint_16)
3031                         mng_info->global_plte[background.index].green;
3032 
3033                       background.blue=(png_uint_16)
3034                         mng_info->global_plte[background.index].blue;
3035 
3036                       background.gray=(png_uint_16)
3037                         mng_info->global_plte[background.index].green;
3038 
3039                       png_set_bKGD(ping,ping_info,&background);
3040                     }
3041 #endif
3042                 }
3043               else
3044                 png_error(ping,"No global PLTE in file");
3045             }
3046         }
3047 
3048 #ifdef PNG_READ_bKGD_SUPPORTED
3049   if (mng_info->have_global_bkgd &&
3050           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
3051       image->background_color=mng_info->mng_global_bkgd;
3052 
3053   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3054     {
3055       unsigned int
3056         bkgd_scale;
3057 
3058       /* Set image background color.
3059        * Scale background components to 16-bit, then scale
3060        * to quantum depth
3061        */
3062 
3063         bkgd_scale = 1;
3064 
3065         if (ping_file_depth == 1)
3066            bkgd_scale = 255;
3067 
3068         else if (ping_file_depth == 2)
3069            bkgd_scale = 85;
3070 
3071         else if (ping_file_depth == 4)
3072            bkgd_scale = 17;
3073 
3074         if (ping_file_depth <= 8)
3075            bkgd_scale *= 257;
3076 
3077         ping_background->red *= bkgd_scale;
3078         ping_background->green *= bkgd_scale;
3079         ping_background->blue *= bkgd_scale;
3080 
3081         if (logging != MagickFalse)
3082           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3083               "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3084               "    bkgd_scale=%d.  ping_background=(%d,%d,%d)",
3085               ping_background->red,ping_background->green,
3086               ping_background->blue,bkgd_scale,ping_background->red,
3087               ping_background->green,ping_background->blue);
3088 
3089         image->background_color.red=
3090             ScaleShortToQuantum(ping_background->red);
3091 
3092         image->background_color.green=
3093             ScaleShortToQuantum(ping_background->green);
3094 
3095         image->background_color.blue=
3096           ScaleShortToQuantum(ping_background->blue);
3097 
3098         image->background_color.alpha=OpaqueAlpha;
3099 
3100         if (logging != MagickFalse)
3101           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3102             "    image->background_color=(%.20g,%.20g,%.20g).",
3103             (double) image->background_color.red,
3104             (double) image->background_color.green,
3105             (double) image->background_color.blue);
3106     }
3107 #endif /* PNG_READ_bKGD_SUPPORTED */
3108 
3109   if ((png_get_valid(ping,ping_info,PNG_INFO_tRNS)) &&
3110       (ping_trans_color != (png_color_16p) NULL))
3111     {
3112       /*
3113         Image has a tRNS chunk.
3114       */
3115       int
3116         max_sample;
3117 
3118       size_t
3119         one = 1;
3120 
3121       if (logging != MagickFalse)
3122         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3123           "    Reading PNG tRNS chunk.");
3124 
3125       max_sample = (int) ((one << ping_file_depth) - 1);
3126 
3127       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3128           (int)ping_trans_color->gray > max_sample) ||
3129           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3130           ((int)ping_trans_color->red > max_sample ||
3131           (int)ping_trans_color->green > max_sample ||
3132           (int)ping_trans_color->blue > max_sample)))
3133         {
3134           if (logging != MagickFalse)
3135             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3136               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3137           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3138           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3139           image->alpha_trait=UndefinedPixelTrait;
3140         }
3141       else
3142         {
3143           int
3144             scale_to_short;
3145 
3146           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3147 
3148           /* Scale transparent_color to short */
3149           transparent_color.red= scale_to_short*ping_trans_color->red;
3150           transparent_color.green= scale_to_short*ping_trans_color->green;
3151           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3152           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3153 
3154           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3155             {
3156               if (logging != MagickFalse)
3157               {
3158                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3159                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3160                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3161 
3162               }
3163               transparent_color.red=transparent_color.alpha;
3164               transparent_color.green=transparent_color.alpha;
3165               transparent_color.blue=transparent_color.alpha;
3166             }
3167         }
3168     }
3169 #if defined(PNG_READ_sBIT_SUPPORTED)
3170   if (mng_info->have_global_sbit)
3171     {
3172       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3173         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3174     }
3175 #endif
3176   num_passes=png_set_interlace_handling(ping);
3177 
3178   png_read_update_info(ping,ping_info);
3179 
3180   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3181 
3182   /*
3183     Initialize image structure.
3184   */
3185   mng_info->image_box.left=0;
3186   mng_info->image_box.right=(ssize_t) ping_width;
3187   mng_info->image_box.top=0;
3188   mng_info->image_box.bottom=(ssize_t) ping_height;
3189   if (mng_info->mng_type == 0)
3190     {
3191       mng_info->mng_width=ping_width;
3192       mng_info->mng_height=ping_height;
3193       mng_info->frame=mng_info->image_box;
3194       mng_info->clip=mng_info->image_box;
3195     }
3196 
3197   else
3198     {
3199       image->page.y=mng_info->y_off[mng_info->object_id];
3200     }
3201 
3202   image->compression=ZipCompression;
3203   image->columns=ping_width;
3204   image->rows=ping_height;
3205 
3206   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3207       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3208     {
3209       double
3210         image_gamma = image->gamma;
3211 
3212       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3213          "    image->gamma=%f",(float) image_gamma);
3214 
3215       if (image_gamma > 0.75)
3216         {
3217           /* Set image->rendering_intent to Undefined,
3218            * image->colorspace to GRAY, and reset image->chromaticity.
3219            */
3220           SetImageColorspace(image,LinearGRAYColorspace,exception);
3221         }
3222       else
3223         {
3224           RenderingIntent
3225             save_rendering_intent = image->rendering_intent;
3226           ChromaticityInfo
3227             save_chromaticity = image->chromaticity;
3228 
3229           SetImageColorspace(image,GRAYColorspace,exception);
3230           image->rendering_intent = save_rendering_intent;
3231           image->chromaticity = save_chromaticity;
3232         }
3233 
3234       image->gamma = image_gamma;
3235     }
3236   else
3237     {
3238       double
3239         image_gamma = image->gamma;
3240 
3241       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3242          "    image->gamma=%f",(float) image_gamma);
3243 
3244       if (image_gamma > 0.75)
3245         {
3246           /* Set image->rendering_intent to Undefined,
3247            * image->colorspace to GRAY, and reset image->chromaticity.
3248            */
3249           image->intensity = Rec709LuminancePixelIntensityMethod;
3250           SetImageColorspace(image,RGBColorspace,exception);
3251         }
3252       else
3253         {
3254           RenderingIntent
3255             save_rendering_intent = image->rendering_intent;
3256           ChromaticityInfo
3257             save_chromaticity = image->chromaticity;
3258 
3259           SetImageColorspace(image,sRGBColorspace,exception);
3260           image->rendering_intent = save_rendering_intent;
3261           image->chromaticity = save_chromaticity;
3262         }
3263 
3264       image->gamma = image_gamma;
3265     }
3266 
3267   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3268       "    image->colorspace=%d",(int) image->colorspace);
3269 
3270   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3271       ((int) ping_bit_depth < 16 &&
3272       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3273     {
3274       size_t
3275         one;
3276 
3277       image->storage_class=PseudoClass;
3278       one=1;
3279       image->colors=one << ping_file_depth;
3280 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3281       if (image->colors > 256)
3282         image->colors=256;
3283 #else
3284       if (image->colors > 65536L)
3285         image->colors=65536L;
3286 #endif
3287       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3288         {
3289           png_colorp
3290             palette;
3291 
3292           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3293           image->colors=(size_t) number_colors;
3294 
3295           if (logging != MagickFalse)
3296             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3297               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3298         }
3299     }
3300 
3301   if (image->storage_class == PseudoClass)
3302     {
3303       /*
3304         Initialize image colormap.
3305       */
3306       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3307         png_error(ping,"Memory allocation failed");
3308 
3309       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3310         {
3311           png_colorp
3312             palette;
3313 
3314           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3315 
3316           for (i=0; i < (ssize_t) number_colors; i++)
3317           {
3318             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3319             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3320             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3321           }
3322 
3323           for ( ; i < (ssize_t) image->colors; i++)
3324           {
3325             image->colormap[i].red=0;
3326             image->colormap[i].green=0;
3327             image->colormap[i].blue=0;
3328           }
3329         }
3330     }
3331 
3332    /* Set some properties for reporting by "identify" */
3333     {
3334       char
3335         msg[MagickPathExtent];
3336 
3337      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3338         ping_interlace_method in value */
3339 
3340      (void) FormatLocaleString(msg,MagickPathExtent,
3341          "%d, %d",(int) ping_width, (int) ping_height);
3342      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3343 
3344      (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_file_depth);
3345      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3346 
3347      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3348          (int) ping_color_type,
3349          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3350      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3351 
3352      if (ping_interlace_method == 0)
3353        {
3354          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3355             (int) ping_interlace_method);
3356        }
3357      else if (ping_interlace_method == 1)
3358        {
3359          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3360             (int) ping_interlace_method);
3361        }
3362      else
3363        {
3364          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3365             (int) ping_interlace_method);
3366        }
3367        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3368 
3369      if (number_colors != 0)
3370        {
3371          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3372             (int) number_colors);
3373          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,exception);
3374        }
3375    }
3376 #if defined(PNG_tIME_SUPPORTED)
3377    read_tIME_chunk(image,ping,ping_info,exception);
3378 #endif
3379 #if defined(PNG_READ_eXIf_SUPPORTED)
3380   read_eXIf_chunk(image,ping,ping_info,exception);
3381 #endif
3382 
3383 
3384   /*
3385     Read image scanlines.
3386   */
3387   if (image->delay != 0)
3388     mng_info->scenes_found++;
3389 
3390   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3391       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3392       (image_info->first_scene+image_info->number_scenes))))
3393     {
3394       /* This happens later in non-ping decodes */
3395       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3396         image->storage_class=DirectClass;
3397       image->alpha_trait=
3398         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3399          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3400          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3401         BlendPixelTrait : UndefinedPixelTrait;
3402 
3403       if (logging != MagickFalse)
3404         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3405           "    Skipping PNG image data for scene %.20g",(double)
3406           mng_info->scenes_found-1);
3407       png_destroy_read_struct(&ping,&ping_info,&end_info);
3408 
3409 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3410       UnlockSemaphoreInfo(ping_semaphore);
3411 #endif
3412 
3413       if (logging != MagickFalse)
3414         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3415           "  exit ReadOnePNGImage().");
3416 
3417       return(image);
3418     }
3419 
3420   if (logging != MagickFalse)
3421     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3422       "    Reading PNG IDAT chunk(s)");
3423 
3424   status=SetImageExtent(image,image->columns,image->rows,exception);
3425   if (status != MagickFalse)
3426     status=ResetImagePixels(image,exception);
3427   if (status == MagickFalse)
3428     {
3429       png_destroy_read_struct(&ping,&ping_info,&end_info);
3430 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3431       UnlockSemaphoreInfo(ping_semaphore);
3432 #endif
3433       return(DestroyImageList(image));
3434     }
3435 
3436   if (num_passes > 1)
3437     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3438       sizeof(*ping_pixels));
3439   else
3440     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3441 
3442   if (pixel_info == (MemoryInfo *) NULL)
3443     png_error(ping,"Memory allocation failed");
3444   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3445 
3446   if (logging != MagickFalse)
3447     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3448       "    Converting PNG pixels to pixel packets");
3449   /*
3450     Convert PNG pixels to pixel packets.
3451   */
3452   quantum_info=AcquireQuantumInfo(image_info,image);
3453 
3454   if (quantum_info == (QuantumInfo *) NULL)
3455      png_error(ping,"Failed to allocate quantum_info");
3456 
3457   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3458 
3459   {
3460 
3461    MagickBooleanType
3462      found_transparent_pixel;
3463 
3464   found_transparent_pixel=MagickFalse;
3465 
3466   if ((image->storage_class == DirectClass) ||
3467       (image_info->stream != (StreamHandler) NULL))
3468     {
3469       for (pass=0; pass < num_passes; pass++)
3470       {
3471         /*
3472           Convert image to DirectClass pixel packets.
3473         */
3474         image->alpha_trait=
3475             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3476             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3477             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3478             BlendPixelTrait : UndefinedPixelTrait;
3479 
3480         for (y=0; y < (ssize_t) image->rows; y++)
3481         {
3482           if (num_passes > 1)
3483             row_offset=ping_rowbytes*y;
3484 
3485           else
3486             row_offset=0;
3487 
3488           png_read_row(ping,ping_pixels+row_offset,NULL);
3489 
3490           if (pass < num_passes-1)
3491             continue;
3492 
3493           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3494 
3495           if (q == (Quantum *) NULL)
3496             break;
3497 
3498           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3499             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3500               GrayQuantum,ping_pixels+row_offset,exception);
3501 
3502           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3503             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3504               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3505 
3506           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3507             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3508               RGBAQuantum,ping_pixels+row_offset,exception);
3509 
3510           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3511             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3512               IndexQuantum,ping_pixels+row_offset,exception);
3513 
3514           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3515             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3516               RGBQuantum,ping_pixels+row_offset,exception);
3517 
3518           if (found_transparent_pixel == MagickFalse)
3519             {
3520               /* Is there a transparent pixel in the row? */
3521               if (y== 0 && logging != MagickFalse)
3522                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3523                    "    Looking for cheap transparent pixel");
3524 
3525               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3526               {
3527                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3528                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3529                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3530                   {
3531                     if (logging != MagickFalse)
3532                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3533                         "    ...got one.");
3534 
3535                     found_transparent_pixel = MagickTrue;
3536                     break;
3537                   }
3538                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3539                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3540                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3541                     transparent_color.red &&
3542                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3543                     transparent_color.green &&
3544                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3545                     transparent_color.blue))
3546                   {
3547                     if (logging != MagickFalse)
3548                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3549                         "    ...got one.");
3550                     found_transparent_pixel = MagickTrue;
3551                     break;
3552                   }
3553                 q+=GetPixelChannels(image);
3554               }
3555             }
3556 
3557           if (num_passes == 1)
3558             {
3559               status=SetImageProgress(image,LoadImageTag,
3560                   (MagickOffsetType) y, image->rows);
3561 
3562               if (status == MagickFalse)
3563                 break;
3564             }
3565           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3566             break;
3567         }
3568         if (y < (long) image->rows)
3569           break;
3570 
3571         if (num_passes != 1)
3572           {
3573             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3574             if (status == MagickFalse)
3575               break;
3576           }
3577       }
3578     }
3579 
3580   else /* image->storage_class != DirectClass */
3581 
3582     for (pass=0; pass < num_passes; pass++)
3583     {
3584       Quantum
3585         *r;
3586 
3587       /*
3588         Convert grayscale image to PseudoClass pixel packets.
3589       */
3590       if (logging != MagickFalse)
3591         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3592           "    Converting grayscale pixels to pixel packets");
3593 
3594       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3595         BlendPixelTrait : UndefinedPixelTrait;
3596 
3597       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3598         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3599         sizeof(*quantum_scanline));
3600 
3601       if (quantum_scanline == (Quantum *) NULL)
3602         png_error(ping,"Memory allocation failed");
3603 
3604       for (y=0; y < (ssize_t) image->rows; y++)
3605       {
3606         Quantum
3607            alpha;
3608 
3609         if (num_passes > 1)
3610           row_offset=ping_rowbytes*y;
3611 
3612         else
3613           row_offset=0;
3614 
3615         png_read_row(ping,ping_pixels+row_offset,NULL);
3616 
3617         if (pass < num_passes-1)
3618           continue;
3619 
3620         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3621 
3622         if (q == (Quantum *) NULL)
3623           break;
3624 
3625         p=ping_pixels+row_offset;
3626         r=quantum_scanline;
3627 
3628         switch (ping_bit_depth)
3629         {
3630           case 8:
3631           {
3632 
3633             if (ping_color_type == 4)
3634               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3635               {
3636                 *r++=*p++;
3637 
3638                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3639 
3640                 SetPixelAlpha(image,alpha,q);
3641 
3642                 if (alpha != OpaqueAlpha)
3643                   found_transparent_pixel = MagickTrue;
3644 
3645                 q+=GetPixelChannels(image);
3646               }
3647 
3648             else
3649               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3650                 *r++=*p++;
3651 
3652             break;
3653           }
3654 
3655           case 16:
3656           {
3657             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3658             {
3659 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3660               unsigned long
3661                 quantum;
3662 
3663               if (image->colors > 256)
3664                 quantum=(((unsigned int) *p++) << 8);
3665 
3666               else
3667                 quantum=0;
3668 
3669               quantum|=(*p++);
3670               *r=ScaleShortToQuantum(quantum);
3671               r++;
3672 
3673               if (ping_color_type == 4)
3674                 {
3675                   if (image->colors > 256)
3676                     quantum=(((unsigned int) *p++) << 8);
3677                   else
3678                     quantum=0;
3679 
3680                   quantum|=(*p++);
3681 
3682                   alpha=ScaleShortToQuantum(quantum);
3683                   SetPixelAlpha(image,alpha,q);
3684 
3685                   if (alpha != OpaqueAlpha)
3686                     found_transparent_pixel = MagickTrue;
3687 
3688                   q+=GetPixelChannels(image);
3689                 }
3690 
3691 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3692               *r++=(*p++);
3693               p++; /* strip low byte */
3694 
3695               if (ping_color_type == 4)
3696                 {
3697                   SetPixelAlpha(image,*p++,q);
3698 
3699                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3700                     found_transparent_pixel = MagickTrue;
3701 
3702                   p++;
3703                   q+=GetPixelChannels(image);
3704                 }
3705 #endif
3706             }
3707 
3708             break;
3709           }
3710 
3711           default:
3712             break;
3713         }
3714 
3715         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3716           break;
3717 
3718         /*
3719           Transfer image scanline.
3720         */
3721         r=quantum_scanline;
3722 
3723         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3724 
3725         if (q == (Quantum *) NULL)
3726           break;
3727         for (x=0; x < (ssize_t) image->columns; x++)
3728         {
3729           ssize_t index=ConstrainColormapIndex(image,(ssize_t) *r,exception);
3730           SetPixelRed(image,ClampToQuantum(image->colormap[index].red),q);
3731           SetPixelGreen(image,ClampToQuantum(image->colormap[index].green),q);
3732           SetPixelBlue(image,ClampToQuantum(image->colormap[index].blue),q);
3733           SetPixelIndex(image,index,q);
3734           r++;
3735           q+=GetPixelChannels(image);
3736         }
3737 
3738         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3739           break;
3740 
3741         if (num_passes == 1)
3742           {
3743             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3744               image->rows);
3745 
3746             if (status == MagickFalse)
3747               break;
3748           }
3749       }
3750       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3751       if (y < (long) image->rows)
3752         break;
3753       if (num_passes != 1)
3754         {
3755           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3756 
3757           if (status == MagickFalse)
3758             break;
3759         }
3760     }
3761 
3762     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3763       UndefinedPixelTrait;
3764 
3765     if (logging != MagickFalse)
3766       {
3767         if (found_transparent_pixel != MagickFalse)
3768           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3769             "    Found transparent pixel");
3770         else
3771           {
3772             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3773               "    No transparent pixel was found");
3774 
3775             ping_color_type&=0x03;
3776           }
3777       }
3778   }
3779   quantum_info=DestroyQuantumInfo(quantum_info);
3780 
3781   png_read_end(ping,end_info);
3782 
3783   if (logging != MagickFalse)
3784   {
3785     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3786        "  image->storage_class=%d\n",(int) image->storage_class);
3787   }
3788 
3789   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3790       (ssize_t) image_info->first_scene && image->delay != 0)
3791     {
3792       png_destroy_read_struct(&ping,&ping_info,&end_info);
3793       pixel_info=RelinquishVirtualMemory(pixel_info);
3794       if (AcquireImageColormap(image,2,exception) == MagickFalse)
3795         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3796       (void) SetImageBackgroundColor(image,exception);
3797 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3798       UnlockSemaphoreInfo(ping_semaphore);
3799 #endif
3800       if (logging != MagickFalse)
3801         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3802           "  exit ReadOnePNGImage() early.");
3803       return(image);
3804     }
3805 
3806   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3807     {
3808       ClassType
3809         storage_class;
3810 
3811       /*
3812         Image has a transparent background.
3813       */
3814       storage_class=image->storage_class;
3815       image->alpha_trait=BlendPixelTrait;
3816 
3817 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3818 
3819       if (storage_class == PseudoClass)
3820         {
3821           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3822             {
3823               for (x=0; x < ping_num_trans; x++)
3824               {
3825                  image->colormap[x].alpha_trait=BlendPixelTrait;
3826                  image->colormap[x].alpha=OpaqueAlpha;
3827                  if (ping_trans_alpha != (png_bytep) NULL)
3828                    image->colormap[x].alpha=ScaleCharToQuantum(
3829                      (unsigned char) ping_trans_alpha[x]);
3830               }
3831             }
3832 
3833           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3834             {
3835               for (x=0; x < (int) image->colors; x++)
3836               {
3837                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3838                      transparent_color.alpha)
3839                  {
3840                     image->colormap[x].alpha_trait=BlendPixelTrait;
3841                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3842                  }
3843               }
3844             }
3845           (void) SyncImage(image,exception);
3846         }
3847 
3848 #if 1 /* Should have already been done above, but glennrp problem P10
3849        * needs this.
3850        */
3851       else
3852         {
3853           for (y=0; y < (ssize_t) image->rows; y++)
3854           {
3855             image->storage_class=storage_class;
3856             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3857 
3858             if (q == (Quantum *) NULL)
3859               break;
3860 
3861 
3862             /* Caution: on a Q8 build, this does not distinguish between
3863              * 16-bit colors that differ only in the low byte
3864              */
3865             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3866             {
3867               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3868                   transparent_color.red &&
3869                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3870                   transparent_color.green &&
3871                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3872                   transparent_color.blue)
3873                 {
3874                   SetPixelAlpha(image,TransparentAlpha,q);
3875                 }
3876               else
3877                 {
3878                   SetPixelAlpha(image,OpaqueAlpha,q);
3879                 }
3880 
3881               q+=GetPixelChannels(image);
3882             }
3883 
3884             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3885                break;
3886           }
3887         }
3888 #endif
3889 
3890       image->storage_class=DirectClass;
3891     }
3892 
3893   for (j = 0; j < 2; j++)
3894   {
3895     if (j == 0)
3896       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3897           MagickTrue : MagickFalse;
3898     else
3899       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3900           MagickTrue : MagickFalse;
3901 
3902     if (status != MagickFalse)
3903       for (i=0; i < (ssize_t) num_text; i++)
3904       {
3905         /* Check for a profile */
3906 
3907         if (logging != MagickFalse)
3908           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3909             "    Reading PNG text chunk");
3910 
3911         if (strlen(text[i].key) > 16 &&
3912             memcmp(text[i].key, "Raw profile type ",17) == 0)
3913           {
3914             const char
3915               *value;
3916 
3917             value=GetImageOption(image_info,"profile:skip");
3918 
3919             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3920             {
3921                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3922                   (int) i,exception);
3923                num_raw_profiles++;
3924                if (logging != MagickFalse)
3925                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3926                    "    Read raw profile %s",text[i].key+17);
3927             }
3928             else
3929             {
3930                if (logging != MagickFalse)
3931                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3932                    "    Skipping raw profile %s",text[i].key+17);
3933             }
3934           }
3935 
3936         else
3937           {
3938             char
3939               *value;
3940 
3941             length=text[i].text_length;
3942             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3943               sizeof(*value));
3944             if (value == (char *) NULL)
3945               {
3946                 png_error(ping,"Memory allocation failed");
3947                 break;
3948               }
3949             *value='\0';
3950             (void) ConcatenateMagickString(value,text[i].text,length+2);
3951 
3952             /* Don't save "density" or "units" property if we have a pHYs
3953              * chunk
3954              */
3955             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3956                 (LocaleCompare(text[i].key,"density") != 0 &&
3957                 LocaleCompare(text[i].key,"units") != 0))
3958               {
3959                 char
3960                   key[MagickPathExtent];
3961 
3962                 (void) FormatLocaleString(key,MagickPathExtent,"%s",
3963                   text[i].key);
3964                 if ((LocaleCompare(key,"version") == 0) ||
3965                     (LocaleCompare(key,"width") == 0))
3966                   (void) FormatLocaleString(key,MagickPathExtent,"png:%s",
3967                     text[i].key);
3968                 (void) SetImageProperty(image,key,value,exception);
3969               }
3970 
3971             if (logging != MagickFalse)
3972             {
3973               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3974                 "      length: %lu\n"
3975                 "      Keyword: %s",
3976                 (unsigned long) length,
3977                 text[i].key);
3978             }
3979 
3980             value=DestroyString(value);
3981           }
3982       }
3983     num_text_total += num_text;
3984   }
3985 
3986 #ifdef MNG_OBJECT_BUFFERS
3987   /*
3988     Store the object if necessary.
3989   */
3990   if (object_id && !mng_info->frozen[object_id])
3991     {
3992       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3993         {
3994           /*
3995             create a new object buffer.
3996           */
3997           mng_info->ob[object_id]=(MngBuffer *)
3998             AcquireMagickMemory(sizeof(MngBuffer));
3999 
4000           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
4001             {
4002               mng_info->ob[object_id]->image=(Image *) NULL;
4003               mng_info->ob[object_id]->reference_count=1;
4004             }
4005         }
4006 
4007       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
4008           mng_info->ob[object_id]->frozen)
4009         {
4010           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
4011              png_error(ping,"Memory allocation failed");
4012 
4013           if (mng_info->ob[object_id]->frozen)
4014             png_error(ping,"Cannot overwrite frozen MNG object buffer");
4015         }
4016 
4017       else
4018         {
4019 
4020           if (mng_info->ob[object_id]->image != (Image *) NULL)
4021             mng_info->ob[object_id]->image=DestroyImage
4022                 (mng_info->ob[object_id]->image);
4023 
4024           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
4025             exception);
4026 
4027           if (mng_info->ob[object_id]->image != (Image *) NULL)
4028             mng_info->ob[object_id]->image->file=(FILE *) NULL;
4029 
4030           else
4031             png_error(ping, "Cloning image for object buffer failed");
4032 
4033           if (ping_width > 250000L || ping_height > 250000L)
4034              png_error(ping,"PNG Image dimensions are too large.");
4035 
4036           mng_info->ob[object_id]->width=ping_width;
4037           mng_info->ob[object_id]->height=ping_height;
4038           mng_info->ob[object_id]->color_type=ping_color_type;
4039           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
4040           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
4041           mng_info->ob[object_id]->compression_method=
4042              ping_compression_method;
4043           mng_info->ob[object_id]->filter_method=ping_filter_method;
4044 
4045           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
4046             {
4047               png_colorp
4048                 plte;
4049 
4050               /*
4051                 Copy the PLTE to the object buffer.
4052               */
4053               png_get_PLTE(ping,ping_info,&plte,&number_colors);
4054               mng_info->ob[object_id]->plte_length=number_colors;
4055 
4056               for (i=0; i < number_colors; i++)
4057               {
4058                 mng_info->ob[object_id]->plte[i]=plte[i];
4059               }
4060             }
4061 
4062           else
4063               mng_info->ob[object_id]->plte_length=0;
4064         }
4065     }
4066 #endif
4067 
4068    /* Set image->alpha_trait to MagickTrue if the input colortype supports
4069     * alpha or if a valid tRNS chunk is present, no matter whether there
4070     * is actual transparency present.
4071     */
4072     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4073         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4074         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4075         BlendPixelTrait : UndefinedPixelTrait;
4076     if (image->alpha_trait == BlendPixelTrait)
4077       (void) SetImageStorageClass(image,DirectClass,exception);
4078 
4079 #if 0  /* I'm not sure what's wrong here but it does not work. */
4080     if (image->alpha_trait != UndefinedPixelTrait)
4081     {
4082       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4083         (void) SetImageType(image,GrayscaleAlphaType,exception);
4084 
4085       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4086         (void) SetImageType(image,PaletteAlphaType,exception);
4087 
4088       else
4089         (void) SetImageType(image,TrueColorAlphaType,exception);
4090     }
4091 
4092     else
4093     {
4094       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4095         (void) SetImageType(image,GrayscaleType,exception);
4096 
4097       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4098         (void) SetImageType(image,PaletteType,exception);
4099 
4100       else
4101         (void) SetImageType(image,TrueColorType,exception);
4102     }
4103 #endif
4104 
4105    /* Set more properties for identify to retrieve */
4106    {
4107      char
4108        msg[MagickPathExtent];
4109 
4110      if (num_text_total != 0)
4111        {
4112          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4113          (void) FormatLocaleString(msg,MagickPathExtent,
4114             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4115          (void) SetImageProperty(image,"png:text",msg,exception);
4116        }
4117 
4118      if (num_raw_profiles != 0)
4119        {
4120          (void) FormatLocaleString(msg,MagickPathExtent,
4121             "%d were found", num_raw_profiles);
4122          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4123                 exception);
4124        }
4125 
4126      /* cHRM chunk: */
4127      if (ping_found_cHRM != MagickFalse)
4128        {
4129          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4130             "chunk was found (see Chromaticity, above)");
4131          (void) SetImageProperty(image,"png:cHRM",msg,exception);
4132        }
4133 
4134      /* bKGD chunk: */
4135      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4136        {
4137          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4138             "chunk was found (see Background color, above)");
4139          (void) SetImageProperty(image,"png:bKGD",msg,exception);
4140        }
4141 
4142      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4143         "chunk was found");
4144 
4145 #if defined(PNG_iCCP_SUPPORTED)
4146      /* iCCP chunk: */
4147      if (ping_found_iCCP != MagickFalse)
4148         (void) SetImageProperty(image,"png:iCCP",msg,exception);
4149 #endif
4150 
4151      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4152         (void) SetImageProperty(image,"png:tRNS",msg,exception);
4153 
4154 #if defined(PNG_sRGB_SUPPORTED)
4155      /* sRGB chunk: */
4156      if (ping_found_sRGB != MagickFalse)
4157        {
4158          (void) FormatLocaleString(msg,MagickPathExtent,
4159             "intent=%d (%s)",
4160             (int) intent,
4161             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4162          (void) SetImageProperty(image,"png:sRGB",msg,exception);
4163        }
4164 #endif
4165 
4166      /* gAMA chunk: */
4167      if (ping_found_gAMA != MagickFalse)
4168        {
4169          (void) FormatLocaleString(msg,MagickPathExtent,
4170             "gamma=%.8g (See Gamma, above)",
4171             file_gamma);
4172          (void) SetImageProperty(image,"png:gAMA",msg,exception);
4173        }
4174 
4175 #if defined(PNG_pHYs_SUPPORTED)
4176      /* pHYs chunk: */
4177      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4178        {
4179          (void) FormatLocaleString(msg,MagickPathExtent,
4180             "x_res=%.10g, y_res=%.10g, units=%d",
4181             (double) x_resolution,(double) y_resolution, unit_type);
4182          (void) SetImageProperty(image,"png:pHYs",msg,exception);
4183        }
4184 #endif
4185 
4186 #if defined(PNG_oFFs_SUPPORTED)
4187      /* oFFs chunk: */
4188      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4189        {
4190          (void) FormatLocaleString(msg,MagickPathExtent,
4191             "x_off=%.20g, y_off=%.20g",
4192             (double) image->page.x,(double) image->page.y);
4193          (void) SetImageProperty(image,"png:oFFs",msg,exception);
4194        }
4195 #endif
4196 
4197 #if defined(PNG_tIME_SUPPORTED)
4198      read_tIME_chunk(image,ping,end_info,exception);
4199 #endif
4200 #if defined(PNG_READ_eXIf_SUPPORTED)
4201     read_eXIf_chunk(image,ping,end_info,exception);
4202 #endif
4203 
4204      /* caNv chunk: */
4205      if ((image->page.width != 0 && image->page.width != image->columns) ||
4206          (image->page.height != 0 && image->page.height != image->rows) ||
4207          (image->page.x != 0 || image->page.y != 0))
4208        {
4209          (void) FormatLocaleString(msg,MagickPathExtent,
4210             "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4211             (double) image->page.width,(double) image->page.height,
4212             (double) image->page.x,(double) image->page.y);
4213          (void) SetImageProperty(image,"png:caNv",msg,exception);
4214        }
4215    }
4216 
4217   /*
4218     Relinquish resources.
4219   */
4220   png_destroy_read_struct(&ping,&ping_info,&end_info);
4221 
4222   pixel_info=RelinquishVirtualMemory(pixel_info);
4223 
4224   if (logging != MagickFalse)
4225     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4226       "  exit ReadOnePNGImage()");
4227 
4228 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4229   UnlockSemaphoreInfo(ping_semaphore);
4230 #endif
4231 
4232   /* }  for navigation to beginning of SETJMP-protected block, revert to
4233    *    Throwing an Exception when an error occurs.
4234    */
4235 
4236   return(image);
4237 
4238 /* end of reading one PNG image */
4239 }
4240 
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)4241 static Image *ReadPNGImage(const ImageInfo *image_info,
4242   ExceptionInfo *exception)
4243 {
4244   Image
4245     *image;
4246 
4247   MagickBooleanType
4248     logging,
4249     status;
4250 
4251   MngInfo
4252     *mng_info;
4253 
4254   char
4255     magic_number[MagickPathExtent];
4256 
4257   ssize_t
4258     count;
4259 
4260   /*
4261     Open image file.
4262   */
4263   assert(image_info != (const ImageInfo *) NULL);
4264   assert(image_info->signature == MagickCoreSignature);
4265 
4266   if (image_info->debug != MagickFalse)
4267     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4268       image_info->filename);
4269 
4270   assert(exception != (ExceptionInfo *) NULL);
4271   assert(exception->signature == MagickCoreSignature);
4272   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4273   image=AcquireImage(image_info,exception);
4274   mng_info=(MngInfo *) NULL;
4275   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4276 
4277   if (status == MagickFalse)
4278     return(DestroyImageList(image));
4279 
4280   /*
4281     Verify PNG signature.
4282   */
4283   count=ReadBlob(image,8,(unsigned char *) magic_number);
4284 
4285   if ((count < 8) || (memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0))
4286     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4287 
4288   /*
4289      Verify that file size large enough to contain a PNG datastream.
4290   */
4291   if (GetBlobSize(image) < 61)
4292     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4293 
4294   /*
4295     Allocate a MngInfo structure.
4296   */
4297   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4298 
4299   if (mng_info == (MngInfo *) NULL)
4300     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4301 
4302   /*
4303     Initialize members of the MngInfo structure.
4304   */
4305   (void) memset(mng_info,0,sizeof(MngInfo));
4306   mng_info->image=image;
4307 
4308   image=ReadOnePNGImage(mng_info,image_info,exception);
4309   mng_info=MngInfoFreeStruct(mng_info);
4310 
4311   if (image == (Image *) NULL)
4312     {
4313       if (logging != MagickFalse)
4314         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4315           "exit ReadPNGImage() with error");
4316 
4317       return((Image *) NULL);
4318     }
4319 
4320   (void) CloseBlob(image);
4321 
4322   if ((image->columns == 0) || (image->rows == 0))
4323     {
4324       if (logging != MagickFalse)
4325         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4326           "exit ReadPNGImage() with error.");
4327 
4328       ThrowReaderException(CorruptImageError,"CorruptImage");
4329     }
4330 
4331   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4332       (image->gamma > .75) &&
4333            !(image->chromaticity.red_primary.x>0.6399f &&
4334            image->chromaticity.red_primary.x<0.6401f &&
4335            image->chromaticity.red_primary.y>0.3299f &&
4336            image->chromaticity.red_primary.y<0.3301f &&
4337            image->chromaticity.green_primary.x>0.2999f &&
4338            image->chromaticity.green_primary.x<0.3001f &&
4339            image->chromaticity.green_primary.y>0.5999f &&
4340            image->chromaticity.green_primary.y<0.6001f &&
4341            image->chromaticity.blue_primary.x>0.1499f &&
4342            image->chromaticity.blue_primary.x<0.1501f &&
4343            image->chromaticity.blue_primary.y>0.0599f &&
4344            image->chromaticity.blue_primary.y<0.0601f &&
4345            image->chromaticity.white_point.x>0.3126f &&
4346            image->chromaticity.white_point.x<0.3128f &&
4347            image->chromaticity.white_point.y>0.3289f &&
4348            image->chromaticity.white_point.y<0.3291f))
4349     {
4350        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4351           "SetImageColorspace to RGBColorspace");
4352        SetImageColorspace(image,RGBColorspace,exception);
4353     }
4354 
4355   if (logging != MagickFalse)
4356     {
4357        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4358            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4359                (double) image->page.width,(double) image->page.height,
4360                (double) image->page.x,(double) image->page.y);
4361        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4362            "  image->colorspace: %d", (int) image->colorspace);
4363     }
4364 
4365   if (logging != MagickFalse)
4366     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4367 
4368   return(image);
4369 }
4370 
4371 
4372 
4373 #if defined(JNG_SUPPORTED)
4374 /*
4375 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4376 %                                                                             %
4377 %                                                                             %
4378 %                                                                             %
4379 %   R e a d O n e J N G I m a g e                                             %
4380 %                                                                             %
4381 %                                                                             %
4382 %                                                                             %
4383 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4384 %
4385 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4386 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4387 %  necessary for the new Image structure and returns a pointer to the new
4388 %  image.
4389 %
4390 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4391 %
4392 %  The format of the ReadOneJNGImage method is:
4393 %
4394 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4395 %         ExceptionInfo *exception)
4396 %
4397 %  A description of each parameter follows:
4398 %
4399 %    o mng_info: Specifies a pointer to a MngInfo structure.
4400 %
4401 %    o image_info: the image info.
4402 %
4403 %    o exception: return any errors or warnings in this structure.
4404 %
4405 */
4406 static void
DestroyJNG(unsigned char * chunk,Image ** color_image,ImageInfo ** color_image_info,Image ** alpha_image,ImageInfo ** alpha_image_info)4407 DestroyJNG(unsigned char *chunk,Image **color_image,
4408   ImageInfo **color_image_info,Image **alpha_image,
4409   ImageInfo **alpha_image_info)
4410 {
4411   (void) RelinquishMagickMemory(chunk);
4412   if (color_image_info && *color_image_info)
4413   {
4414     DestroyImageInfo(*color_image_info);
4415     *color_image_info = (ImageInfo *)NULL;
4416   }
4417   if (alpha_image_info && *alpha_image_info)
4418   {
4419     DestroyImageInfo(*alpha_image_info);
4420     *alpha_image_info = (ImageInfo *)NULL;
4421   }
4422   if (color_image && *color_image)
4423   {
4424     DestroyImageList(*color_image);
4425     *color_image = (Image *)NULL;
4426   }
4427   if (alpha_image && *alpha_image)
4428   {
4429     DestroyImageList(*alpha_image);
4430     *alpha_image = (Image *)NULL;
4431   }
4432 }
ReadOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)4433 static Image *ReadOneJNGImage(MngInfo *mng_info,
4434     const ImageInfo *image_info, ExceptionInfo *exception)
4435 {
4436   Image
4437     *alpha_image,
4438     *color_image,
4439     *image,
4440     *jng_image;
4441 
4442   ImageInfo
4443     *alpha_image_info,
4444     *color_image_info;
4445 
4446   MagickBooleanType
4447     logging;
4448 
4449   ssize_t
4450     y;
4451 
4452   MagickBooleanType
4453     status;
4454 
4455   png_uint_32
4456     jng_height,
4457     jng_width;
4458 
4459   png_byte
4460     jng_color_type,
4461     jng_image_sample_depth,
4462     jng_image_compression_method,
4463     jng_image_interlace_method,
4464     jng_alpha_sample_depth,
4465     jng_alpha_compression_method,
4466     jng_alpha_filter_method,
4467     jng_alpha_interlace_method;
4468 
4469   const Quantum
4470     *s;
4471 
4472   ssize_t
4473     i,
4474     x;
4475 
4476   Quantum
4477     *q;
4478 
4479   unsigned char
4480     *p;
4481 
4482   unsigned int
4483     read_JSEP,
4484     reading_idat;
4485 
4486   size_t
4487     length;
4488 
4489   jng_alpha_compression_method=0;
4490   jng_alpha_sample_depth=8;
4491   jng_color_type=0;
4492   jng_height=0;
4493   jng_width=0;
4494   alpha_image=(Image *) NULL;
4495   color_image=(Image *) NULL;
4496   alpha_image_info=(ImageInfo *) NULL;
4497   color_image_info=(ImageInfo *) NULL;
4498 
4499   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4500     "  Enter ReadOneJNGImage()");
4501 
4502   image=mng_info->image;
4503 
4504   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4505     {
4506       /*
4507         Allocate next image structure.
4508       */
4509       if (logging != MagickFalse)
4510         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4511            "  AcquireNextImage()");
4512 
4513       AcquireNextImage(image_info,image,exception);
4514 
4515       if (GetNextImageInList(image) == (Image *) NULL)
4516         return(DestroyImageList(image));
4517 
4518       image=SyncNextImageInList(image);
4519     }
4520   mng_info->image=image;
4521 
4522   /*
4523     Signature bytes have already been read.
4524   */
4525 
4526   read_JSEP=MagickFalse;
4527   reading_idat=MagickFalse;
4528   for (;;)
4529   {
4530     char
4531       type[MagickPathExtent];
4532 
4533     unsigned char
4534       *chunk;
4535 
4536     unsigned int
4537       count;
4538 
4539     /*
4540       Read a new JNG chunk.
4541     */
4542     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4543       2*GetBlobSize(image));
4544 
4545     if (status == MagickFalse)
4546       break;
4547 
4548     type[0]='\0';
4549     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4550     length=(size_t) ReadBlobMSBLong(image);
4551     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4552 
4553     if (logging != MagickFalse)
4554       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4555         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4556         type[0],type[1],type[2],type[3],(double) length);
4557 
4558     if (length > PNG_UINT_31_MAX || count == 0)
4559       {
4560         DestroyJNG(NULL,&color_image,&color_image_info,
4561           &alpha_image,&alpha_image_info);
4562         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4563       }
4564     if (length > GetBlobSize(image))
4565       {
4566         DestroyJNG(NULL,&color_image,&color_image_info,
4567           &alpha_image,&alpha_image_info);
4568         ThrowReaderException(CorruptImageError,
4569           "InsufficientImageDataInFile");
4570       }
4571 
4572     p=NULL;
4573     chunk=(unsigned char *) NULL;
4574 
4575     if (length != 0)
4576       {
4577         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4578 
4579         if (chunk == (unsigned char *) NULL)
4580           {
4581             DestroyJNG(NULL,&color_image,&color_image_info,
4582               &alpha_image,&alpha_image_info);
4583             ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4584           }
4585 
4586         for (i=0; i < (ssize_t) length; i++)
4587         {
4588           int
4589             c;
4590 
4591           c=ReadBlobByte(image);
4592           if (c == EOF)
4593             break;
4594           chunk[i]=(unsigned char) c;
4595         }
4596         for ( ; i < (ssize_t) length; i++)
4597           chunk[i]='\0';
4598 
4599         p=chunk;
4600       }
4601 
4602     (void) ReadBlobMSBLong(image);  /* read crc word */
4603 
4604     if (memcmp(type,mng_JHDR,4) == 0)
4605       {
4606         if (length == 16)
4607           {
4608             jng_width=(png_uint_32)mng_get_long(p);
4609             jng_height=(png_uint_32)mng_get_long(&p[4]);
4610             if ((jng_width == 0) || (jng_height == 0))
4611               {
4612                 DestroyJNG(chunk,&color_image,&color_image_info,
4613                   &alpha_image,&alpha_image_info);
4614                 ThrowReaderException(CorruptImageError,
4615                   "NegativeOrZeroImageSize");
4616               }
4617             jng_color_type=p[8];
4618             jng_image_sample_depth=p[9];
4619             jng_image_compression_method=p[10];
4620             jng_image_interlace_method=p[11];
4621 
4622             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4623               NoInterlace;
4624 
4625             jng_alpha_sample_depth=p[12];
4626             jng_alpha_compression_method=p[13];
4627             jng_alpha_filter_method=p[14];
4628             jng_alpha_interlace_method=p[15];
4629 
4630             if (logging != MagickFalse)
4631               {
4632                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4633                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4634                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4635                   "    jng_image_compression_method:%3d",
4636                   (unsigned long) jng_width, (unsigned long) jng_height,
4637                   jng_color_type, jng_image_sample_depth,
4638                   jng_image_compression_method);
4639 
4640                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4641                   "    jng_image_interlace_method:  %3d"
4642                   "    jng_alpha_sample_depth:      %3d",
4643                   jng_image_interlace_method,
4644                   jng_alpha_sample_depth);
4645 
4646                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4647                   "    jng_alpha_compression_method:%3d\n"
4648                   "    jng_alpha_filter_method:     %3d\n"
4649                   "    jng_alpha_interlace_method:  %3d",
4650                   jng_alpha_compression_method,
4651                   jng_alpha_filter_method,
4652                   jng_alpha_interlace_method);
4653               }
4654           }
4655 
4656         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4657 
4658         if ((jng_width > 65535) || (jng_height > 65535) ||
4659             (MagickSizeType) jng_width > GetMagickResourceLimit(WidthResource) ||
4660             (MagickSizeType) jng_height > GetMagickResourceLimit(HeightResource))
4661           {
4662             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4663                "    JNG width or height too large: (%lu x %lu)",
4664                 (long) jng_width, (long) jng_height);
4665             DestroyJNG(chunk,&color_image,&color_image_info,
4666               &alpha_image,&alpha_image_info);
4667             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4668           }
4669 
4670         continue;
4671       }
4672 
4673 
4674     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4675         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4676          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4677       {
4678         /*
4679            o create color_image
4680            o open color_blob, attached to color_image
4681            o if (color type has alpha)
4682                open alpha_blob, attached to alpha_image
4683         */
4684 
4685         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4686 
4687         if (color_image_info == (ImageInfo *) NULL)
4688         {
4689           DestroyJNG(chunk,&color_image,&color_image_info,
4690               &alpha_image,&alpha_image_info);
4691           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4692         }
4693 
4694         GetImageInfo(color_image_info);
4695         color_image=AcquireImage(color_image_info,exception);
4696 
4697         if (color_image == (Image *) NULL)
4698         {
4699           DestroyJNG(chunk,&color_image,&color_image_info,
4700               &alpha_image,&alpha_image_info);
4701           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4702         }
4703 
4704         if (logging != MagickFalse)
4705           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4706             "    Creating color_blob.");
4707 
4708         (void) AcquireUniqueFilename(color_image->filename);
4709         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4710           exception);
4711 
4712         if (status == MagickFalse)
4713           {
4714             DestroyJNG(chunk,&color_image,&color_image_info,
4715               &alpha_image,&alpha_image_info);
4716             return(DestroyImageList(image));
4717           }
4718 
4719         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4720           {
4721             if ((jng_alpha_compression_method != 0) &&
4722                 (jng_alpha_compression_method != 8))
4723               {
4724                 DestroyJNG(chunk,&color_image,&color_image_info,&alpha_image,
4725                   &alpha_image_info);
4726                 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4727               }
4728             alpha_image_info=(ImageInfo *)
4729               AcquireMagickMemory(sizeof(ImageInfo));
4730 
4731             if (alpha_image_info == (ImageInfo *) NULL)
4732               {
4733                 DestroyJNG(chunk,&color_image,&color_image_info,
4734                   &alpha_image,&alpha_image_info);
4735                 ThrowReaderException(ResourceLimitError,
4736                   "MemoryAllocationFailed");
4737               }
4738 
4739             GetImageInfo(alpha_image_info);
4740             alpha_image=AcquireImage(alpha_image_info,exception);
4741 
4742             if (alpha_image == (Image *) NULL)
4743               {
4744                 DestroyJNG(chunk,&color_image,&color_image_info,
4745                   &alpha_image,&alpha_image_info);
4746                 ThrowReaderException(ResourceLimitError,
4747                   "MemoryAllocationFailed");
4748               }
4749 
4750             if (logging != MagickFalse)
4751               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4752                 "    Creating alpha_blob.");
4753 
4754             (void) AcquireUniqueFilename(alpha_image->filename);
4755             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4756               exception);
4757 
4758             if (status == MagickFalse)
4759               {
4760                 DestroyJNG(chunk,&color_image,&color_image_info,
4761                   &alpha_image,&alpha_image_info);
4762                 return(DestroyImageList(image));
4763               }
4764 
4765             if (jng_alpha_compression_method == 0)
4766               {
4767                 unsigned char
4768                   data[18];
4769 
4770                 if (logging != MagickFalse)
4771                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4772                     "    Writing IHDR chunk to alpha_blob.");
4773 
4774                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4775                   "\211PNG\r\n\032\n");
4776 
4777                 (void) WriteBlobMSBULong(alpha_image,13L);
4778                 PNGType(data,mng_IHDR);
4779                 LogPNGChunk(logging,mng_IHDR,13L);
4780                 PNGLong(data+4,jng_width);
4781                 PNGLong(data+8,jng_height);
4782                 data[12]=jng_alpha_sample_depth;
4783                 data[13]=0; /* color_type gray */
4784                 data[14]=0; /* compression method 0 */
4785                 data[15]=0; /* filter_method 0 */
4786                 data[16]=0; /* interlace_method 0 */
4787                 (void) WriteBlob(alpha_image,17,data);
4788                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4789               }
4790           }
4791         reading_idat=MagickTrue;
4792       }
4793 
4794     if (memcmp(type,mng_JDAT,4) == 0)
4795       {
4796         /* Copy chunk to color_image->blob */
4797 
4798         if (logging != MagickFalse)
4799           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4800             "    Copying JDAT chunk data to color_blob.");
4801 
4802         if ((length != 0) && (color_image != (Image *) NULL))
4803           (void) WriteBlob(color_image,length,chunk);
4804         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4805         continue;
4806       }
4807 
4808     if (memcmp(type,mng_IDAT,4) == 0)
4809       {
4810         png_byte
4811            data[5];
4812 
4813         /* Copy IDAT header and chunk data to alpha_image->blob */
4814 
4815         if (alpha_image != NULL && image_info->ping == MagickFalse)
4816           {
4817             if (logging != MagickFalse)
4818               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4819                 "    Copying IDAT chunk data to alpha_blob.");
4820 
4821             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4822             PNGType(data,mng_IDAT);
4823             LogPNGChunk(logging,mng_IDAT,length);
4824             (void) WriteBlob(alpha_image,4,data);
4825             (void) WriteBlob(alpha_image,length,chunk);
4826             (void) WriteBlobMSBULong(alpha_image,
4827               crc32(crc32(0,data,4),chunk,(uInt) length));
4828           }
4829 
4830         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4831 
4832         continue;
4833       }
4834 
4835     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4836       {
4837         /* Copy chunk data to alpha_image->blob */
4838 
4839         if ((alpha_image != NULL) && (image_info->ping == MagickFalse) &&
4840             (length != 0))
4841           {
4842             if (logging != MagickFalse)
4843               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4844                 "    Copying JDAA chunk data to alpha_blob.");
4845 
4846             (void) WriteBlob(alpha_image,length,chunk);
4847           }
4848 
4849         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4850 
4851         continue;
4852       }
4853 
4854     if (memcmp(type,mng_JSEP,4) == 0)
4855       {
4856         read_JSEP=MagickTrue;
4857 
4858         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4859 
4860         continue;
4861       }
4862 
4863     if (memcmp(type,mng_bKGD,4) == 0)
4864       {
4865         if (length == 2)
4866           {
4867             image->background_color.red=ScaleCharToQuantum(p[1]);
4868             image->background_color.green=image->background_color.red;
4869             image->background_color.blue=image->background_color.red;
4870           }
4871 
4872         if (length == 6)
4873           {
4874             image->background_color.red=ScaleCharToQuantum(p[1]);
4875             image->background_color.green=ScaleCharToQuantum(p[3]);
4876             image->background_color.blue=ScaleCharToQuantum(p[5]);
4877           }
4878 
4879         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4880         continue;
4881       }
4882 
4883     if (memcmp(type,mng_gAMA,4) == 0)
4884       {
4885         if (length == 4)
4886           image->gamma=((float) mng_get_long(p))*0.00001;
4887 
4888         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4889         continue;
4890       }
4891 
4892     if (memcmp(type,mng_cHRM,4) == 0)
4893       {
4894         if (length == 32)
4895           {
4896             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4897             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4898             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4899             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4900             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4901             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4902             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4903             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4904           }
4905 
4906         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4907         continue;
4908       }
4909 
4910     if (memcmp(type,mng_sRGB,4) == 0)
4911       {
4912         if (length == 1)
4913           {
4914             image->rendering_intent=
4915               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4916             image->gamma=0.45455f;
4917             image->chromaticity.red_primary.x=0.6400f;
4918             image->chromaticity.red_primary.y=0.3300f;
4919             image->chromaticity.green_primary.x=0.3000f;
4920             image->chromaticity.green_primary.y=0.6000f;
4921             image->chromaticity.blue_primary.x=0.1500f;
4922             image->chromaticity.blue_primary.y=0.0600f;
4923             image->chromaticity.white_point.x=0.3127f;
4924             image->chromaticity.white_point.y=0.3290f;
4925           }
4926 
4927         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4928         continue;
4929       }
4930 
4931     if (memcmp(type,mng_oFFs,4) == 0)
4932       {
4933         if (length > 8)
4934           {
4935             image->page.x=(ssize_t) mng_get_long(p);
4936             image->page.y=(ssize_t) mng_get_long(&p[4]);
4937 
4938             if ((int) p[8] != 0)
4939               {
4940                 image->page.x/=10000;
4941                 image->page.y/=10000;
4942               }
4943           }
4944 
4945         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4946 
4947         continue;
4948       }
4949 
4950     if (memcmp(type,mng_pHYs,4) == 0)
4951       {
4952         if (length > 8)
4953           {
4954             image->resolution.x=(double) mng_get_long(p);
4955             image->resolution.y=(double) mng_get_long(&p[4]);
4956             if ((int) p[8] == PNG_RESOLUTION_METER)
4957               {
4958                 image->units=PixelsPerCentimeterResolution;
4959                 image->resolution.x=image->resolution.x/100.0f;
4960                 image->resolution.y=image->resolution.y/100.0f;
4961               }
4962           }
4963 
4964         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4965         continue;
4966       }
4967 
4968 #if 0
4969     if (memcmp(type,mng_iCCP,4) == 0)
4970       {
4971         /* To do: */
4972         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4973 
4974         continue;
4975       }
4976 #endif
4977 
4978     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4979 
4980     if (memcmp(type,mng_IEND,4))
4981       continue;
4982 
4983     break;
4984   }
4985 
4986 
4987   /* IEND found */
4988 
4989   /*
4990     Finish up reading image data:
4991 
4992        o read main image from color_blob.
4993 
4994        o close color_blob.
4995 
4996        o if (color_type has alpha)
4997             if alpha_encoding is PNG
4998                read secondary image from alpha_blob via ReadPNG
4999             if alpha_encoding is JPEG
5000                read secondary image from alpha_blob via ReadJPEG
5001 
5002        o close alpha_blob.
5003 
5004        o copy intensity of secondary image into
5005          alpha samples of main image.
5006 
5007        o destroy the secondary image.
5008   */
5009 
5010   if (color_image_info == (ImageInfo *) NULL)
5011     {
5012       assert(color_image == (Image *) NULL);
5013       assert(alpha_image == (Image *) NULL);
5014       if (color_image != (Image *) NULL)
5015         color_image=DestroyImageList(color_image);
5016       return(DestroyImageList(image));
5017     }
5018 
5019   if (color_image == (Image *) NULL)
5020     {
5021       assert(alpha_image == (Image *) NULL);
5022       ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5023     }
5024 
5025   (void) SeekBlob(color_image,0,SEEK_SET);
5026 
5027   if (logging != MagickFalse)
5028     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5029       "    Reading jng_image from color_blob.");
5030 
5031   assert(color_image_info != (ImageInfo *) NULL);
5032   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,
5033     "jpeg:%s",color_image->filename);
5034 
5035   color_image_info->ping=MagickFalse;   /* To do: avoid this */
5036   jng_image=ReadImage(color_image_info,exception);
5037 
5038   (void) RelinquishUniqueFileResource(color_image->filename);
5039   color_image=DestroyImageList(color_image);
5040   color_image_info=DestroyImageInfo(color_image_info);
5041 
5042   if (jng_image == (Image *) NULL)
5043   {
5044     DestroyJNG(NULL,NULL,NULL,&alpha_image,&alpha_image_info);
5045     return(DestroyImageList(image));
5046   }
5047 
5048 
5049   if (logging != MagickFalse)
5050     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5051       "    Copying jng_image pixels to main image.");
5052 
5053   image->rows=jng_height;
5054   image->columns=jng_width;
5055 
5056   status=SetImageExtent(image,image->columns,image->rows,exception);
5057   if (status == MagickFalse)
5058     {
5059       DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5060         &alpha_image_info);
5061       jng_image=DestroyImageList(jng_image);
5062       return(DestroyImageList(image));
5063     }
5064   if ((image->columns != jng_image->columns) ||
5065       (image->rows != jng_image->rows))
5066     {
5067       DestroyJNG(NULL,&color_image,&color_image_info,&alpha_image,
5068         &alpha_image_info);
5069       jng_image=DestroyImageList(jng_image);
5070       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5071     }
5072   for (y=0; y < (ssize_t) image->rows; y++)
5073   {
5074     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5075     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5076     if ((s == (const Quantum *)  NULL) || (q == (Quantum *) NULL))
5077       break;
5078     for (x=(ssize_t) image->columns; x != 0; x--)
5079     {
5080       SetPixelRed(image,GetPixelRed(jng_image,s),q);
5081       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
5082       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
5083       q+=GetPixelChannels(image);
5084       s+=GetPixelChannels(jng_image);
5085     }
5086 
5087     if (SyncAuthenticPixels(image,exception) == MagickFalse)
5088       break;
5089   }
5090 
5091   jng_image=DestroyImage(jng_image);
5092 
5093   if ((image_info->ping == MagickFalse) && (alpha_image != (Image *) NULL) &&
5094       (jng_color_type >= 12))
5095     {
5096       if (jng_alpha_compression_method == 0)
5097         {
5098           png_byte
5099             data[5];
5100           (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5101           PNGType(data,mng_IEND);
5102           LogPNGChunk(logging,mng_IEND,0L);
5103           (void) WriteBlob(alpha_image,4,data);
5104           (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5105         }
5106 
5107       (void) CloseBlob(alpha_image);
5108 
5109       if (logging != MagickFalse)
5110         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5111           "    Reading alpha from alpha_blob.");
5112 
5113       (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5114         "%s",alpha_image->filename);
5115 
5116       jng_image=ReadImage(alpha_image_info,exception);
5117 
5118       if (jng_image != (Image *) NULL)
5119         for (y=0; y < (ssize_t) image->rows; y++)
5120         {
5121           s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
5122           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5123           if ((s == (const Quantum *)  NULL) || (q == (Quantum *) NULL))
5124             break;
5125 
5126           if (image->alpha_trait != UndefinedPixelTrait)
5127             for (x=(ssize_t) image->columns; x != 0; x--)
5128             {
5129               SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5130               q+=GetPixelChannels(image);
5131               s+=GetPixelChannels(jng_image);
5132             }
5133 
5134           else
5135             for (x=(ssize_t) image->columns; x != 0; x--)
5136             {
5137               SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5138               if (GetPixelAlpha(image,q) != OpaqueAlpha)
5139                 image->alpha_trait=BlendPixelTrait;
5140               q+=GetPixelChannels(image);
5141               s+=GetPixelChannels(jng_image);
5142             }
5143 
5144           if (SyncAuthenticPixels(image,exception) == MagickFalse)
5145             break;
5146         }
5147       (void) RelinquishUniqueFileResource(alpha_image->filename);
5148       alpha_image=DestroyImageList(alpha_image);
5149       alpha_image_info=DestroyImageInfo(alpha_image_info);
5150       if (jng_image != (Image *) NULL)
5151         jng_image=DestroyImageList(jng_image);
5152     }
5153 
5154   /* Read the JNG image.  */
5155 
5156   if (mng_info->mng_type == 0)
5157     {
5158       mng_info->mng_width=jng_width;
5159       mng_info->mng_height=jng_height;
5160     }
5161 
5162   if (image->page.width == 0 && image->page.height == 0)
5163     {
5164       image->page.width=jng_width;
5165       image->page.height=jng_height;
5166     }
5167 
5168   if (image->page.x == 0 && image->page.y == 0)
5169     {
5170       image->page.x=mng_info->x_off[mng_info->object_id];
5171       image->page.y=mng_info->y_off[mng_info->object_id];
5172     }
5173 
5174   else
5175     {
5176       image->page.y=mng_info->y_off[mng_info->object_id];
5177     }
5178 
5179   mng_info->image_found++;
5180   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5181     2*GetBlobSize(image));
5182 
5183   if (status == MagickFalse)
5184     return(DestroyImageList(image));
5185 
5186   if (logging != MagickFalse)
5187     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5188       "  exit ReadOneJNGImage()");
5189 
5190   return(image);
5191 }
5192 
5193 /*
5194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5195 %                                                                             %
5196 %                                                                             %
5197 %                                                                             %
5198 %   R e a d J N G I m a g e                                                   %
5199 %                                                                             %
5200 %                                                                             %
5201 %                                                                             %
5202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5203 %
5204 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5205 %  (including the 8-byte signature)  and returns it.  It allocates the memory
5206 %  necessary for the new Image structure and returns a pointer to the new
5207 %  image.
5208 %
5209 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
5210 %
5211 %  The format of the ReadJNGImage method is:
5212 %
5213 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5214 %         *exception)
5215 %
5216 %  A description of each parameter follows:
5217 %
5218 %    o image_info: the image info.
5219 %
5220 %    o exception: return any errors or warnings in this structure.
5221 %
5222 */
5223 
ReadJNGImage(const ImageInfo * image_info,ExceptionInfo * exception)5224 static Image *ReadJNGImage(const ImageInfo *image_info,
5225                 ExceptionInfo *exception)
5226 {
5227   Image
5228     *image;
5229 
5230   MagickBooleanType
5231     logging,
5232     status;
5233 
5234   MngInfo
5235     *mng_info;
5236 
5237   char
5238     magic_number[MagickPathExtent];
5239 
5240   size_t
5241     count;
5242 
5243   /*
5244     Open image file.
5245   */
5246   assert(image_info != (const ImageInfo *) NULL);
5247   assert(image_info->signature == MagickCoreSignature);
5248   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5249      image_info->filename);
5250   assert(exception != (ExceptionInfo *) NULL);
5251   assert(exception->signature == MagickCoreSignature);
5252   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5253   image=AcquireImage(image_info,exception);
5254   mng_info=(MngInfo *) NULL;
5255   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5256 
5257   if (status == MagickFalse)
5258     return(DestroyImageList(image));
5259 
5260   if (LocaleCompare(image_info->magick,"JNG") != 0)
5261     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5262 
5263   /* Verify JNG signature.  */
5264 
5265   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5266 
5267   if ((count < 8) || (memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0))
5268     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5269 
5270   /*
5271      Verify that file size large enough to contain a JNG datastream.
5272   */
5273   if (GetBlobSize(image) < 147)
5274     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5275 
5276   /* Allocate a MngInfo structure.  */
5277 
5278   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5279 
5280   if (mng_info == (MngInfo *) NULL)
5281     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5282 
5283   /* Initialize members of the MngInfo structure.  */
5284 
5285   (void) memset(mng_info,0,sizeof(MngInfo));
5286 
5287   mng_info->image=image;
5288   image=ReadOneJNGImage(mng_info,image_info,exception);
5289   mng_info=MngInfoFreeStruct(mng_info);
5290 
5291   if (image == (Image *) NULL)
5292     {
5293       if (logging != MagickFalse)
5294         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5295           "exit ReadJNGImage() with error");
5296 
5297       return((Image *) NULL);
5298     }
5299   (void) CloseBlob(image);
5300 
5301   if (image->columns == 0 || image->rows == 0)
5302     {
5303       if (logging != MagickFalse)
5304         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5305           "exit ReadJNGImage() with error");
5306 
5307       ThrowReaderException(CorruptImageError,"CorruptImage");
5308     }
5309 
5310   if (logging != MagickFalse)
5311     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5312 
5313   return(image);
5314 }
5315 #endif
5316 
ReadOneMNGImage(MngInfo * mng_info,const ImageInfo * image_info,ExceptionInfo * exception)5317 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5318      ExceptionInfo *exception)
5319 {
5320   char
5321     page_geometry[MagickPathExtent];
5322 
5323   Image
5324     *image;
5325 
5326   MagickBooleanType
5327     logging;
5328 
5329   volatile int
5330     first_mng_object,
5331     object_id,
5332     term_chunk_found,
5333     skip_to_iend;
5334 
5335   volatile ssize_t
5336     image_count=0;
5337 
5338   MagickBooleanType
5339     status;
5340 
5341   MagickOffsetType
5342     offset;
5343 
5344   MngBox
5345     default_fb,
5346     fb,
5347     previous_fb;
5348 
5349 #if defined(MNG_INSERT_LAYERS)
5350   PixelInfo
5351     mng_background_color;
5352 #endif
5353 
5354   unsigned char
5355     *p;
5356 
5357   ssize_t
5358     i;
5359 
5360   size_t
5361     count;
5362 
5363   ssize_t
5364     loop_level;
5365 
5366   volatile short
5367     skipping_loop;
5368 
5369 #if defined(MNG_INSERT_LAYERS)
5370   unsigned int
5371     mandatory_back=0;
5372 #endif
5373 
5374   volatile unsigned int
5375 #ifdef MNG_OBJECT_BUFFERS
5376     mng_background_object=0,
5377 #endif
5378     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5379 
5380   size_t
5381     default_frame_timeout,
5382     frame_timeout,
5383 #if defined(MNG_INSERT_LAYERS)
5384     image_height,
5385     image_width,
5386 #endif
5387     length;
5388 
5389   /* These delays are all measured in image ticks_per_second,
5390    * not in MNG ticks_per_second
5391    */
5392   volatile size_t
5393     default_frame_delay,
5394     final_delay,
5395     final_image_delay,
5396     frame_delay,
5397 #if defined(MNG_INSERT_LAYERS)
5398     insert_layers,
5399 #endif
5400     mng_iterations=1,
5401     simplicity=0,
5402     subframe_height=0,
5403     subframe_width=0;
5404 
5405   previous_fb.top=0;
5406   previous_fb.bottom=0;
5407   previous_fb.left=0;
5408   previous_fb.right=0;
5409   default_fb.top=0;
5410   default_fb.bottom=0;
5411   default_fb.left=0;
5412   default_fb.right=0;
5413 
5414   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5415     "  Enter ReadOneMNGImage()");
5416 
5417   image=mng_info->image;
5418 
5419   if (LocaleCompare(image_info->magick,"MNG") == 0)
5420     {
5421       char
5422         magic_number[MagickPathExtent];
5423 
5424       /* Verify MNG signature.  */
5425       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5426       if ((count < 8) || (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0))
5427         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5428 
5429       /* Initialize some nonzero members of the MngInfo structure.  */
5430       for (i=0; i < MNG_MAX_OBJECTS; i++)
5431       {
5432         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5433         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5434       }
5435       mng_info->exists[0]=MagickTrue;
5436     }
5437 
5438   skipping_loop=(-1);
5439   first_mng_object=MagickTrue;
5440   mng_type=0;
5441 #if defined(MNG_INSERT_LAYERS)
5442   insert_layers=MagickFalse; /* should be False during convert or mogrify */
5443 #endif
5444   default_frame_delay=0;
5445   default_frame_timeout=0;
5446   frame_delay=0;
5447   final_delay=1;
5448   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5449   object_id=0;
5450   skip_to_iend=MagickFalse;
5451   term_chunk_found=MagickFalse;
5452   mng_info->framing_mode=1;
5453 #if defined(MNG_INSERT_LAYERS)
5454   mandatory_back=MagickFalse;
5455 #endif
5456 #if defined(MNG_INSERT_LAYERS)
5457   mng_background_color=image->background_color;
5458 #endif
5459   default_fb=mng_info->frame;
5460   previous_fb=mng_info->frame;
5461   do
5462   {
5463     char
5464       type[MagickPathExtent];
5465 
5466     if (LocaleCompare(image_info->magick,"MNG") == 0)
5467       {
5468         unsigned char
5469           *chunk;
5470 
5471         /*
5472           Read a new chunk.
5473         */
5474         type[0]='\0';
5475         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5476         length=(size_t) ReadBlobMSBLong(image);
5477         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5478 
5479         if (logging != MagickFalse)
5480           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5481            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5482            type[0],type[1],type[2],type[3],(double) length);
5483 
5484         if ((length > PNG_UINT_31_MAX) || (length > GetBlobSize(image)) ||
5485             (count < 4))
5486           ThrowReaderException(CorruptImageError,"CorruptImage");
5487 
5488         p=NULL;
5489         chunk=(unsigned char *) NULL;
5490 
5491         if (length != 0)
5492           {
5493             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5494 
5495             if (chunk == (unsigned char *) NULL)
5496               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5497 
5498             for (i=0; i < (ssize_t) length; i++)
5499             {
5500               int
5501                 c;
5502 
5503               c=ReadBlobByte(image);
5504               if (c == EOF)
5505                 {
5506                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5507                   ThrowReaderException(CorruptImageError,
5508                     "InsufficientImageDataInFile");
5509                 }
5510               chunk[i]=(unsigned char) c;
5511             }
5512 
5513             p=chunk;
5514           }
5515 
5516         (void) ReadBlobMSBLong(image);  /* read crc word */
5517 
5518 #if !defined(JNG_SUPPORTED)
5519         if (memcmp(type,mng_JHDR,4) == 0)
5520           {
5521             skip_to_iend=MagickTrue;
5522 
5523             if (mng_info->jhdr_warning == 0)
5524               (void) ThrowMagickException(exception,GetMagickModule(),
5525                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5526 
5527             mng_info->jhdr_warning++;
5528           }
5529 #endif
5530         if (memcmp(type,mng_DHDR,4) == 0)
5531           {
5532             skip_to_iend=MagickTrue;
5533 
5534             if (mng_info->dhdr_warning == 0)
5535               (void) ThrowMagickException(exception,GetMagickModule(),
5536                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5537 
5538             mng_info->dhdr_warning++;
5539           }
5540         if (memcmp(type,mng_MEND,4) == 0)
5541           {
5542             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5543             break;
5544           }
5545 
5546         if (skip_to_iend)
5547           {
5548             if (memcmp(type,mng_IEND,4) == 0)
5549               skip_to_iend=MagickFalse;
5550 
5551             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5552 
5553             if (logging != MagickFalse)
5554               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5555                 "  Skip to IEND.");
5556 
5557             continue;
5558           }
5559 
5560         if (memcmp(type,mng_MHDR,4) == 0)
5561           {
5562             if (length != 28)
5563               {
5564                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5565                 ThrowReaderException(CorruptImageError,"CorruptImage");
5566               }
5567 
5568             mng_info->mng_width=(unsigned long)mng_get_long(p);
5569             mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5570 
5571             if (logging != MagickFalse)
5572               {
5573                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5574                   "  MNG width: %.20g",(double) mng_info->mng_width);
5575                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5576                   "  MNG height: %.20g",(double) mng_info->mng_height);
5577               }
5578 
5579             p+=8;
5580             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5581 
5582             if (mng_info->ticks_per_second == 0)
5583               default_frame_delay=0;
5584 
5585             else
5586               default_frame_delay=1UL*image->ticks_per_second/
5587                 mng_info->ticks_per_second;
5588 
5589             frame_delay=default_frame_delay;
5590             simplicity=0;
5591 
5592             p+=16;
5593             simplicity=(size_t) mng_get_long(p);
5594 
5595             mng_type=1;    /* Full MNG */
5596 
5597             if ((simplicity != 0) && ((simplicity | 11) == 11))
5598               mng_type=2; /* LC */
5599 
5600             if ((simplicity != 0) && ((simplicity | 9) == 9))
5601               mng_type=3; /* VLC */
5602 
5603 #if defined(MNG_INSERT_LAYERS)
5604             if (mng_type != 3)
5605               insert_layers=MagickTrue;
5606 #endif
5607             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5608               {
5609                 /* Allocate next image structure.  */
5610                 AcquireNextImage(image_info,image,exception);
5611 
5612                 if (GetNextImageInList(image) == (Image *) NULL)
5613                   return((Image *) NULL);
5614 
5615                 image=SyncNextImageInList(image);
5616                 mng_info->image=image;
5617               }
5618 
5619             if ((mng_info->mng_width > 65535L) ||
5620                 (mng_info->mng_height > 65535L))
5621               {
5622                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5623                 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5624               }
5625 
5626             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5627               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5628               mng_info->mng_height);
5629 
5630             mng_info->frame.left=0;
5631             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5632             mng_info->frame.top=0;
5633             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5634             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5635 
5636             for (i=0; i < MNG_MAX_OBJECTS; i++)
5637               mng_info->object_clip[i]=mng_info->frame;
5638 
5639             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5640             continue;
5641           }
5642 
5643         if (memcmp(type,mng_TERM,4) == 0)
5644           {
5645             int
5646               repeat=0;
5647 
5648             if (length != 0)
5649               repeat=p[0];
5650 
5651             if (repeat == 3 && length > 9)
5652               {
5653                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5654                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5655 
5656                 if (mng_iterations == PNG_UINT_31_MAX)
5657                   mng_iterations=0;
5658 
5659                 image->iterations=mng_iterations;
5660                 term_chunk_found=MagickTrue;
5661               }
5662 
5663             if (logging != MagickFalse)
5664               {
5665                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5666                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5667                   repeat,(double) final_delay, (double) image->iterations);
5668               }
5669 
5670             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5671             continue;
5672           }
5673         if (memcmp(type,mng_DEFI,4) == 0)
5674           {
5675             if (mng_type == 3)
5676               {
5677                 (void) ThrowMagickException(exception,GetMagickModule(),
5678                   CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5679                   image->filename);
5680                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5681                 continue;
5682               }
5683 
5684             if (length < 2)
5685               {
5686                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5687                 ThrowReaderException(CorruptImageError,"CorruptImage");
5688               }
5689 
5690             object_id=((unsigned int) p[0] << 8) | (unsigned int) p[1];
5691 
5692             if (mng_type == 2 && object_id != 0)
5693               (void) ThrowMagickException(exception,GetMagickModule(),
5694                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5695                 image->filename);
5696 
5697             if (object_id >= MNG_MAX_OBJECTS)
5698               {
5699                 /*
5700                   Instead of using a warning we should allocate a larger
5701                   MngInfo structure and continue.
5702                 */
5703                 (void) ThrowMagickException(exception,GetMagickModule(),
5704                   CoderError,"object id too large","`%s'",image->filename);
5705                 object_id=MNG_MAX_OBJECTS-1;
5706               }
5707 
5708             if (mng_info->exists[object_id])
5709               if (mng_info->frozen[object_id])
5710                 {
5711                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5712                   (void) ThrowMagickException(exception,
5713                     GetMagickModule(),CoderError,
5714                     "DEFI cannot redefine a frozen MNG object","`%s'",
5715                     image->filename);
5716                   continue;
5717                 }
5718 
5719             mng_info->exists[object_id]=MagickTrue;
5720 
5721             if (length > 2)
5722               mng_info->invisible[object_id]=p[2];
5723 
5724             /*
5725               Extract object offset info.
5726             */
5727             if (length > 11)
5728               {
5729                 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5730                 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5731                 if (logging != MagickFalse)
5732                   {
5733                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5734                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5735                       object_id,(double) mng_info->x_off[object_id],
5736                       object_id,(double) mng_info->y_off[object_id]);
5737                   }
5738               }
5739 
5740             /*
5741               Extract object clipping info.
5742             */
5743             if (length > 27)
5744               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5745                 &p[12]);
5746 
5747             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5748             continue;
5749           }
5750         if (memcmp(type,mng_bKGD,4) == 0)
5751           {
5752             mng_info->have_global_bkgd=MagickFalse;
5753 
5754             if (length > 5)
5755               {
5756                 mng_info->mng_global_bkgd.red=
5757                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5758 
5759                 mng_info->mng_global_bkgd.green=
5760                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5761 
5762                 mng_info->mng_global_bkgd.blue=
5763                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5764 
5765                 mng_info->have_global_bkgd=MagickTrue;
5766               }
5767 
5768             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5769             continue;
5770           }
5771         if (memcmp(type,mng_BACK,4) == 0)
5772           {
5773 #if defined(MNG_INSERT_LAYERS)
5774             if (length > 6)
5775               mandatory_back=p[6];
5776 
5777             else
5778               mandatory_back=0;
5779 
5780             if (mandatory_back && length > 5)
5781               {
5782                 mng_background_color.red=
5783                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5784 
5785                 mng_background_color.green=
5786                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5787 
5788                 mng_background_color.blue=
5789                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5790 
5791                 mng_background_color.alpha=OpaqueAlpha;
5792               }
5793 
5794 #ifdef MNG_OBJECT_BUFFERS
5795             if (length > 8)
5796               mng_background_object=(p[7] << 8) | p[8];
5797 #endif
5798 #endif
5799             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5800             continue;
5801           }
5802 
5803         if (memcmp(type,mng_PLTE,4) == 0)
5804           {
5805             /* Read global PLTE.  */
5806 
5807             if (length && (length < 769))
5808               {
5809                 /* Read global PLTE.  */
5810 
5811                 if (mng_info->global_plte == (png_colorp) NULL)
5812                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5813                     sizeof(*mng_info->global_plte));
5814 
5815                 if (mng_info->global_plte == (png_colorp) NULL)
5816                   {
5817                     mng_info->global_plte_length=0;
5818                     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5819                     ThrowReaderException(ResourceLimitError,
5820                       "MemoryAllocationFailed");
5821                   }
5822 
5823                 for (i=0; i < (ssize_t) (length/3); i++)
5824                 {
5825                   mng_info->global_plte[i].red=p[3*i];
5826                   mng_info->global_plte[i].green=p[3*i+1];
5827                   mng_info->global_plte[i].blue=p[3*i+2];
5828                 }
5829 
5830                 mng_info->global_plte_length=(unsigned int) (length/3);
5831               }
5832 #ifdef MNG_LOOSE
5833             for ( ; i < 256; i++)
5834             {
5835               mng_info->global_plte[i].red=i;
5836               mng_info->global_plte[i].green=i;
5837               mng_info->global_plte[i].blue=i;
5838             }
5839 
5840             if (length != 0)
5841               mng_info->global_plte_length=256;
5842 #endif
5843             else
5844               mng_info->global_plte_length=0;
5845 
5846             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5847             continue;
5848           }
5849 
5850         if (memcmp(type,mng_tRNS,4) == 0)
5851           {
5852             /* read global tRNS */
5853 
5854             if (length > 0 && length < 257)
5855               for (i=0; i < (ssize_t) length; i++)
5856                 mng_info->global_trns[i]=p[i];
5857 
5858 #ifdef MNG_LOOSE
5859             for ( ; i < 256; i++)
5860               mng_info->global_trns[i]=255;
5861 #endif
5862             mng_info->global_trns_length=(unsigned int) length;
5863             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5864             continue;
5865           }
5866         if (memcmp(type,mng_gAMA,4) == 0)
5867           {
5868             if (length == 4)
5869               {
5870                 ssize_t
5871                   igamma;
5872 
5873                 igamma=mng_get_long(p);
5874                 mng_info->global_gamma=((float) igamma)*0.00001;
5875                 mng_info->have_global_gama=MagickTrue;
5876               }
5877 
5878             else
5879               mng_info->have_global_gama=MagickFalse;
5880 
5881             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5882             continue;
5883           }
5884 
5885         if (memcmp(type,mng_cHRM,4) == 0)
5886           {
5887             /* Read global cHRM */
5888 
5889             if (length == 32)
5890               {
5891                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5892                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5893                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5894                 mng_info->global_chrm.red_primary.y=0.00001*
5895                   mng_get_long(&p[12]);
5896                 mng_info->global_chrm.green_primary.x=0.00001*
5897                   mng_get_long(&p[16]);
5898                 mng_info->global_chrm.green_primary.y=0.00001*
5899                   mng_get_long(&p[20]);
5900                 mng_info->global_chrm.blue_primary.x=0.00001*
5901                   mng_get_long(&p[24]);
5902                 mng_info->global_chrm.blue_primary.y=0.00001*
5903                   mng_get_long(&p[28]);
5904                 mng_info->have_global_chrm=MagickTrue;
5905               }
5906             else
5907               mng_info->have_global_chrm=MagickFalse;
5908 
5909             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5910             continue;
5911           }
5912 
5913         if (memcmp(type,mng_sRGB,4) == 0)
5914           {
5915             /*
5916               Read global sRGB.
5917             */
5918             if (length != 0)
5919               {
5920                 mng_info->global_srgb_intent=
5921                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5922                 mng_info->have_global_srgb=MagickTrue;
5923               }
5924             else
5925               mng_info->have_global_srgb=MagickFalse;
5926 
5927             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5928             continue;
5929           }
5930 
5931         if (memcmp(type,mng_iCCP,4) == 0)
5932           {
5933             /* To do: */
5934 
5935             /*
5936               Read global iCCP.
5937             */
5938             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5939 
5940             continue;
5941           }
5942 
5943         if (memcmp(type,mng_FRAM,4) == 0)
5944           {
5945             if (mng_type == 3)
5946               (void) ThrowMagickException(exception,GetMagickModule(),
5947                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5948                 image->filename);
5949 
5950             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5951               image->delay=frame_delay;
5952 
5953             frame_delay=default_frame_delay;
5954             frame_timeout=default_frame_timeout;
5955             fb=default_fb;
5956 
5957             if (length != 0)
5958               if (p[0])
5959                 mng_info->framing_mode=p[0];
5960 
5961             if (logging != MagickFalse)
5962               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5963                 "    Framing_mode=%d",mng_info->framing_mode);
5964 
5965             if (length > 6)
5966               {
5967                 /* Note the delay and frame clipping boundaries.  */
5968 
5969                 p++; /* framing mode */
5970 
5971                 while (((p-chunk) < (long) length) && *p)
5972                   p++;  /* frame name */
5973 
5974                 p++;  /* frame name terminator */
5975 
5976                 if ((p-chunk) < (ssize_t) (length-4))
5977                   {
5978                     int
5979                       change_delay,
5980                       change_timeout,
5981                       change_clipping;
5982 
5983                     change_delay=(*p++);
5984                     change_timeout=(*p++);
5985                     change_clipping=(*p++);
5986                     p++; /* change_sync */
5987 
5988                     if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5989                       {
5990                         frame_delay=1UL*image->ticks_per_second*
5991                           mng_get_long(p);
5992 
5993                         if (mng_info->ticks_per_second != 0)
5994                           frame_delay/=mng_info->ticks_per_second;
5995 
5996                         else
5997                           frame_delay=PNG_UINT_31_MAX;
5998 
5999                         if (change_delay == 2)
6000                           default_frame_delay=frame_delay;
6001 
6002                         p+=4;
6003 
6004                         if (logging != MagickFalse)
6005                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6006                             "    Framing_delay=%.20g",(double) frame_delay);
6007                       }
6008 
6009                     if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
6010                       {
6011                         frame_timeout=1UL*image->ticks_per_second*
6012                           mng_get_long(p);
6013 
6014                         if (mng_info->ticks_per_second != 0)
6015                           frame_timeout/=mng_info->ticks_per_second;
6016 
6017                         else
6018                           frame_timeout=PNG_UINT_31_MAX;
6019 
6020                         if (change_timeout == 2)
6021                           default_frame_timeout=frame_timeout;
6022 
6023                         p+=4;
6024 
6025                         if (logging != MagickFalse)
6026                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6027                             "    Framing_timeout=%.20g",(double) frame_timeout);
6028                       }
6029 
6030                     if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
6031                       {
6032                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
6033                         p+=16;
6034                         previous_fb=fb;
6035 
6036                         if (logging != MagickFalse)
6037                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6038                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
6039                             (double) fb.left,(double) fb.right,(double) fb.top,
6040                             (double) fb.bottom);
6041 
6042                         if (change_clipping == 2)
6043                           default_fb=fb;
6044                       }
6045                   }
6046               }
6047             mng_info->clip=fb;
6048             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
6049 
6050             subframe_width=(size_t) (mng_info->clip.right
6051                -mng_info->clip.left);
6052 
6053             subframe_height=(size_t) (mng_info->clip.bottom
6054                -mng_info->clip.top);
6055             /*
6056               Insert a background layer behind the frame if framing_mode is 4.
6057             */
6058 #if defined(MNG_INSERT_LAYERS)
6059             if (logging != MagickFalse)
6060               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6061                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
6062                 subframe_width,(double) subframe_height);
6063 
6064             if (insert_layers && (mng_info->framing_mode == 4) &&
6065                 (subframe_width) && (subframe_height))
6066               {
6067                 /* Allocate next image structure.  */
6068                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6069                   {
6070                     AcquireNextImage(image_info,image,exception);
6071 
6072                     if (GetNextImageInList(image) == (Image *) NULL)
6073                       return(DestroyImageList(image));
6074 
6075                     image=SyncNextImageInList(image);
6076                   }
6077 
6078                 mng_info->image=image;
6079 
6080                 if (term_chunk_found)
6081                   {
6082                     image->start_loop=MagickTrue;
6083                     image->iterations=mng_iterations;
6084                     term_chunk_found=MagickFalse;
6085                   }
6086 
6087                 else
6088                     image->start_loop=MagickFalse;
6089 
6090                 image->columns=subframe_width;
6091                 image->rows=subframe_height;
6092                 image->page.width=subframe_width;
6093                 image->page.height=subframe_height;
6094                 image->page.x=mng_info->clip.left;
6095                 image->page.y=mng_info->clip.top;
6096                 image->background_color=mng_background_color;
6097                 image->alpha_trait=UndefinedPixelTrait;
6098                 image->delay=0;
6099                 if (SetImageBackgroundColor(image,exception) == MagickFalse)
6100                   {
6101                     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6102                     return(DestroyImageList(image));
6103                   }
6104                 if (logging != MagickFalse)
6105                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6106                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6107                     (double) mng_info->clip.left,
6108                     (double) mng_info->clip.right,
6109                     (double) mng_info->clip.top,
6110                     (double) mng_info->clip.bottom);
6111               }
6112 #endif
6113             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6114             continue;
6115           }
6116 
6117         if (memcmp(type,mng_CLIP,4) == 0)
6118           {
6119             unsigned int
6120               first_object,
6121               last_object;
6122 
6123             /*
6124               Read CLIP.
6125             */
6126             if (length > 3)
6127               {
6128                 first_object=(p[0] << 8) | p[1];
6129                 last_object=(p[2] << 8) | p[3];
6130                 p+=4;
6131 
6132                 for (i=(int) first_object; i <= (int) last_object; i++)
6133                 {
6134                   if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6135                     continue;
6136 
6137                   if (mng_info->exists[i] && !mng_info->frozen[i])
6138                     {
6139                       MngBox
6140                         box;
6141 
6142                       box=mng_info->object_clip[i];
6143                       if ((p-chunk) < (ssize_t) (length-17))
6144                         mng_info->object_clip[i]=
6145                            mng_read_box(box,(char) p[0],&p[1]);
6146                     }
6147                 }
6148 
6149               }
6150             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6151             continue;
6152           }
6153 
6154         if (memcmp(type,mng_SAVE,4) == 0)
6155           {
6156             for (i=1; i < MNG_MAX_OBJECTS; i++)
6157               if (mng_info->exists[i])
6158                 {
6159                  mng_info->frozen[i]=MagickTrue;
6160 #ifdef MNG_OBJECT_BUFFERS
6161                  if (mng_info->ob[i] != (MngBuffer *) NULL)
6162                     mng_info->ob[i]->frozen=MagickTrue;
6163 #endif
6164                 }
6165 
6166             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6167 
6168             continue;
6169           }
6170 
6171         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6172           {
6173             /* Read DISC or SEEK.  */
6174 
6175             if ((length == 0) || (length % 2) || !memcmp(type,mng_SEEK,4))
6176               {
6177                 for (i=1; i < MNG_MAX_OBJECTS; i++)
6178                   MngInfoDiscardObject(mng_info,i);
6179               }
6180 
6181             else
6182               {
6183                 ssize_t
6184                   j;
6185 
6186                 for (j=1; j < (ssize_t) length; j+=2)
6187                 {
6188                   i=p[j-1] << 8 | p[j];
6189                   MngInfoDiscardObject(mng_info,i);
6190                 }
6191               }
6192 
6193             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6194 
6195             continue;
6196           }
6197 
6198         if (memcmp(type,mng_MOVE,4) == 0)
6199           {
6200             size_t
6201               first_object,
6202               last_object;
6203 
6204             /* read MOVE */
6205 
6206             if (length > 3)
6207             {
6208               first_object=(p[0] << 8) | p[1];
6209               last_object=(p[2] << 8) | p[3];
6210               p+=4;
6211 
6212               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6213               {
6214                 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6215                   continue;
6216 
6217                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6218                     (p-chunk) < (ssize_t) (length-8))
6219                   {
6220                     MngPair
6221                       new_pair;
6222 
6223                     MngPair
6224                       old_pair;
6225 
6226                     old_pair.a=mng_info->x_off[i];
6227                     old_pair.b=mng_info->y_off[i];
6228                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6229                     mng_info->x_off[i]=new_pair.a;
6230                     mng_info->y_off[i]=new_pair.b;
6231                   }
6232               }
6233             }
6234 
6235             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6236             continue;
6237           }
6238 
6239         if (memcmp(type,mng_LOOP,4) == 0)
6240           {
6241             ssize_t loop_iters=1;
6242             if (length > 4)
6243               {
6244                 loop_level=chunk[0];
6245                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
6246 
6247                 /* Record starting point.  */
6248                 loop_iters=mng_get_long(&chunk[1]);
6249 
6250                 if (logging != MagickFalse)
6251                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6252                     "  LOOP level %.20g has %.20g iterations ",
6253                     (double) loop_level, (double) loop_iters);
6254 
6255                 if (loop_iters <= 0)
6256                   skipping_loop=loop_level;
6257 
6258                 else
6259                   {
6260                     if ((MagickSizeType) loop_iters > GetMagickResourceLimit(ListLengthResource))
6261                       loop_iters=GetMagickResourceLimit(ListLengthResource);
6262                     if (loop_iters >= 2147483647L)
6263                       loop_iters=2147483647L;
6264                     mng_info->loop_jump[loop_level]=TellBlob(image);
6265                     mng_info->loop_count[loop_level]=loop_iters;
6266                   }
6267 
6268                 mng_info->loop_iteration[loop_level]=0;
6269               }
6270             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6271             continue;
6272           }
6273 
6274         if (memcmp(type,mng_ENDL,4) == 0)
6275           {
6276             if (length > 0)
6277               {
6278                 loop_level=chunk[0];
6279 
6280                 if (skipping_loop > 0)
6281                   {
6282                     if (skipping_loop == loop_level)
6283                       {
6284                         /*
6285                           Found end of zero-iteration loop.
6286                         */
6287                         skipping_loop=(-1);
6288                         mng_info->loop_active[loop_level]=0;
6289                       }
6290                   }
6291 
6292                 else
6293                   {
6294                     if (mng_info->loop_active[loop_level] == 1)
6295                       {
6296                         mng_info->loop_count[loop_level]--;
6297                         mng_info->loop_iteration[loop_level]++;
6298 
6299                         if (logging != MagickFalse)
6300                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6301                           "  ENDL: LOOP level %.20g has %.20g remaining iters",
6302                             (double) loop_level,(double)
6303                             mng_info->loop_count[loop_level]);
6304 
6305                         if (mng_info->loop_count[loop_level] > 0)
6306                           {
6307                             offset=
6308                               SeekBlob(image,mng_info->loop_jump[loop_level],
6309                               SEEK_SET);
6310 
6311                             if (offset < 0)
6312                               {
6313                                 chunk=(unsigned char *) RelinquishMagickMemory(
6314                                   chunk);
6315                                 ThrowReaderException(CorruptImageError,
6316                                   "ImproperImageHeader");
6317                               }
6318                           }
6319 
6320                         else
6321                           {
6322                             short
6323                               last_level;
6324 
6325                             /*
6326                               Finished loop.
6327                             */
6328                             mng_info->loop_active[loop_level]=0;
6329                             last_level=(-1);
6330                             for (i=0; i < loop_level; i++)
6331                               if (mng_info->loop_active[i] == 1)
6332                                 last_level=(short) i;
6333                             loop_level=last_level;
6334                           }
6335                       }
6336                   }
6337               }
6338 
6339             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6340             continue;
6341           }
6342 
6343         if (memcmp(type,mng_CLON,4) == 0)
6344           {
6345             if (mng_info->clon_warning == 0)
6346               (void) ThrowMagickException(exception,GetMagickModule(),
6347                 CoderError,"CLON is not implemented yet","`%s'",
6348                 image->filename);
6349 
6350             mng_info->clon_warning++;
6351           }
6352 
6353         if (memcmp(type,mng_MAGN,4) == 0)
6354           {
6355             png_uint_16
6356               magn_first,
6357               magn_last,
6358               magn_mb,
6359               magn_ml,
6360               magn_mr,
6361               magn_mt,
6362               magn_mx,
6363               magn_my,
6364               magn_methx,
6365               magn_methy;
6366 
6367             if (length > 1)
6368               magn_first=(p[0] << 8) | p[1];
6369 
6370             else
6371               magn_first=0;
6372 
6373             if (length > 3)
6374               magn_last=(p[2] << 8) | p[3];
6375 
6376             else
6377               magn_last=magn_first;
6378 #ifndef MNG_OBJECT_BUFFERS
6379             if (magn_first || magn_last)
6380               if (mng_info->magn_warning == 0)
6381                 {
6382                   (void) ThrowMagickException(exception,
6383                      GetMagickModule(),CoderError,
6384                      "MAGN is not implemented yet for nonzero objects",
6385                      "`%s'",image->filename);
6386 
6387                    mng_info->magn_warning++;
6388                 }
6389 #endif
6390             if (length > 4)
6391               magn_methx=p[4];
6392 
6393             else
6394               magn_methx=0;
6395 
6396             if (length > 6)
6397               magn_mx=(p[5] << 8) | p[6];
6398 
6399             else
6400               magn_mx=1;
6401 
6402             if (magn_mx == 0)
6403               magn_mx=1;
6404 
6405             if (length > 8)
6406               magn_my=(p[7] << 8) | p[8];
6407 
6408             else
6409               magn_my=magn_mx;
6410 
6411             if (magn_my == 0)
6412               magn_my=1;
6413 
6414             if (length > 10)
6415               magn_ml=(p[9] << 8) | p[10];
6416 
6417             else
6418               magn_ml=magn_mx;
6419 
6420             if (magn_ml == 0)
6421               magn_ml=1;
6422 
6423             if (length > 12)
6424               magn_mr=(p[11] << 8) | p[12];
6425 
6426             else
6427               magn_mr=magn_mx;
6428 
6429             if (magn_mr == 0)
6430               magn_mr=1;
6431 
6432             if (length > 14)
6433               magn_mt=(p[13] << 8) | p[14];
6434 
6435             else
6436               magn_mt=magn_my;
6437 
6438             if (magn_mt == 0)
6439               magn_mt=1;
6440 
6441             if (length > 16)
6442               magn_mb=(p[15] << 8) | p[16];
6443 
6444             else
6445               magn_mb=magn_my;
6446 
6447             if (magn_mb == 0)
6448               magn_mb=1;
6449 
6450             if (length > 17)
6451               magn_methy=p[17];
6452 
6453             else
6454               magn_methy=magn_methx;
6455 
6456 
6457             if (magn_methx > 5 || magn_methy > 5)
6458               if (mng_info->magn_warning == 0)
6459                 {
6460                   (void) ThrowMagickException(exception,
6461                      GetMagickModule(),CoderError,
6462                      "Unknown MAGN method in MNG datastream","`%s'",
6463                      image->filename);
6464 
6465                    mng_info->magn_warning++;
6466                 }
6467 #ifdef MNG_OBJECT_BUFFERS
6468           /* Magnify existing objects in the range magn_first to magn_last */
6469 #endif
6470             if (magn_first == 0 || magn_last == 0)
6471               {
6472                 /* Save the magnification factors for object 0 */
6473                 mng_info->magn_mb=magn_mb;
6474                 mng_info->magn_ml=magn_ml;
6475                 mng_info->magn_mr=magn_mr;
6476                 mng_info->magn_mt=magn_mt;
6477                 mng_info->magn_mx=magn_mx;
6478                 mng_info->magn_my=magn_my;
6479                 mng_info->magn_methx=magn_methx;
6480                 mng_info->magn_methy=magn_methy;
6481               }
6482           }
6483 
6484         if (memcmp(type,mng_PAST,4) == 0)
6485           {
6486             if (mng_info->past_warning == 0)
6487               (void) ThrowMagickException(exception,GetMagickModule(),
6488                 CoderError,"PAST is not implemented yet","`%s'",
6489                 image->filename);
6490 
6491             mng_info->past_warning++;
6492           }
6493 
6494         if (memcmp(type,mng_SHOW,4) == 0)
6495           {
6496             if (mng_info->show_warning == 0)
6497               (void) ThrowMagickException(exception,GetMagickModule(),
6498                 CoderError,"SHOW is not implemented yet","`%s'",
6499                 image->filename);
6500 
6501             mng_info->show_warning++;
6502           }
6503 
6504         if (memcmp(type,mng_sBIT,4) == 0)
6505           {
6506             if (length < 4)
6507               mng_info->have_global_sbit=MagickFalse;
6508 
6509             else
6510               {
6511                 mng_info->global_sbit.gray=p[0];
6512                 mng_info->global_sbit.red=p[0];
6513                 mng_info->global_sbit.green=p[1];
6514                 mng_info->global_sbit.blue=p[2];
6515                 mng_info->global_sbit.alpha=p[3];
6516                 mng_info->have_global_sbit=MagickTrue;
6517              }
6518           }
6519         if (memcmp(type,mng_pHYs,4) == 0)
6520           {
6521             if (length > 8)
6522               {
6523                 mng_info->global_x_pixels_per_unit=
6524                     (size_t) mng_get_long(p);
6525                 mng_info->global_y_pixels_per_unit=
6526                     (size_t) mng_get_long(&p[4]);
6527                 mng_info->global_phys_unit_type=p[8];
6528                 mng_info->have_global_phys=MagickTrue;
6529               }
6530 
6531             else
6532               mng_info->have_global_phys=MagickFalse;
6533           }
6534         if (memcmp(type,mng_pHYg,4) == 0)
6535           {
6536             if (mng_info->phyg_warning == 0)
6537               (void) ThrowMagickException(exception,GetMagickModule(),
6538                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6539 
6540             mng_info->phyg_warning++;
6541           }
6542         if (memcmp(type,mng_BASI,4) == 0)
6543           {
6544             skip_to_iend=MagickTrue;
6545 
6546             if (mng_info->basi_warning == 0)
6547               (void) ThrowMagickException(exception,GetMagickModule(),
6548                 CoderError,"BASI is not implemented yet","`%s'",
6549                 image->filename);
6550 
6551             mng_info->basi_warning++;
6552 #ifdef MNG_BASI_SUPPORTED
6553             basi_width=(unsigned long) mng_get_long(p);
6554             basi_width=(unsigned long) mng_get_long(&p[4]);
6555             basi_color_type=p[8];
6556             basi_compression_method=p[9];
6557             basi_filter_type=p[10];
6558             basi_interlace_method=p[11];
6559             if (length > 11)
6560               basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6561 
6562             else
6563               basi_red=0;
6564 
6565             if (length > 13)
6566               basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6567 
6568             else
6569               basi_green=0;
6570 
6571             if (length > 15)
6572               basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6573 
6574             else
6575               basi_blue=0;
6576 
6577             if (length > 17)
6578               basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6579 
6580             else
6581               {
6582                 if (basi_sample_depth == 16)
6583                   basi_alpha=65535L;
6584                 else
6585                   basi_alpha=255;
6586               }
6587 
6588             if (length > 19)
6589               basi_viewable=p[20];
6590 
6591             else
6592               basi_viewable=0;
6593 
6594 #endif
6595             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6596             continue;
6597           }
6598 
6599         if (memcmp(type,mng_IHDR,4)
6600 #if defined(JNG_SUPPORTED)
6601             && memcmp(type,mng_JHDR,4)
6602 #endif
6603             )
6604           {
6605             /* Not an IHDR or JHDR chunk */
6606             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6607 
6608             continue;
6609           }
6610 /* Process IHDR */
6611         if (logging != MagickFalse)
6612           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6613             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6614 
6615         mng_info->exists[object_id]=MagickTrue;
6616         mng_info->viewable[object_id]=MagickTrue;
6617 
6618         if (mng_info->invisible[object_id])
6619           {
6620             if (logging != MagickFalse)
6621               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6622                 "  Skipping invisible object");
6623 
6624             skip_to_iend=MagickTrue;
6625             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6626             continue;
6627           }
6628 #if defined(MNG_INSERT_LAYERS)
6629         if (length < 8)
6630           {
6631             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6632             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6633           }
6634 
6635         image_width=(size_t) mng_get_long(p);
6636         image_height=(size_t) mng_get_long(&p[4]);
6637 #endif
6638         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6639 
6640         /*
6641           Insert a transparent background layer behind the entire animation
6642           if it is not full screen.
6643         */
6644 #if defined(MNG_INSERT_LAYERS)
6645         if (insert_layers && mng_type && first_mng_object)
6646           {
6647             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6648                 (image_width < mng_info->mng_width) ||
6649                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6650                 (image_height < mng_info->mng_height) ||
6651                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6652               {
6653                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6654                   {
6655                     /*
6656                       Allocate next image structure.
6657                     */
6658                     AcquireNextImage(image_info,image,exception);
6659 
6660                     if (GetNextImageInList(image) == (Image *) NULL)
6661                       return(DestroyImageList(image));
6662 
6663                     image=SyncNextImageInList(image);
6664                   }
6665                 mng_info->image=image;
6666 
6667                 if (term_chunk_found)
6668                   {
6669                     image->start_loop=MagickTrue;
6670                     image->iterations=mng_iterations;
6671                     term_chunk_found=MagickFalse;
6672                   }
6673 
6674                 else
6675                     image->start_loop=MagickFalse;
6676 
6677                 /* Make a background rectangle.  */
6678 
6679                 image->delay=0;
6680                 image->columns=mng_info->mng_width;
6681                 image->rows=mng_info->mng_height;
6682                 image->page.width=mng_info->mng_width;
6683                 image->page.height=mng_info->mng_height;
6684                 image->page.x=0;
6685                 image->page.y=0;
6686                 image->background_color=mng_background_color;
6687                 (void) SetImageBackgroundColor(image,exception);
6688                 if (logging != MagickFalse)
6689                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6690                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6691                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6692               }
6693           }
6694         /*
6695           Insert a background layer behind the upcoming image if
6696           framing_mode is 3, and we haven't already inserted one.
6697         */
6698         if (insert_layers && (mng_info->framing_mode == 3) &&
6699                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6700                 (simplicity & 0x08)))
6701           {
6702             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6703             {
6704               /*
6705                 Allocate next image structure.
6706               */
6707               AcquireNextImage(image_info,image,exception);
6708 
6709               if (GetNextImageInList(image) == (Image *) NULL)
6710                 return(DestroyImageList(image));
6711 
6712               image=SyncNextImageInList(image);
6713             }
6714 
6715             mng_info->image=image;
6716 
6717             if (term_chunk_found)
6718               {
6719                 image->start_loop=MagickTrue;
6720                 image->iterations=mng_iterations;
6721                 term_chunk_found=MagickFalse;
6722               }
6723 
6724             else
6725                 image->start_loop=MagickFalse;
6726 
6727             image->delay=0;
6728             image->columns=subframe_width;
6729             image->rows=subframe_height;
6730             image->page.width=subframe_width;
6731             image->page.height=subframe_height;
6732             image->page.x=mng_info->clip.left;
6733             image->page.y=mng_info->clip.top;
6734             image->background_color=mng_background_color;
6735             image->alpha_trait=UndefinedPixelTrait;
6736             (void) SetImageBackgroundColor(image,exception);
6737 
6738             if (logging != MagickFalse)
6739               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6740                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6741                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6742                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6743           }
6744 #endif /* MNG_INSERT_LAYERS */
6745         first_mng_object=MagickFalse;
6746 
6747         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6748           {
6749             /*
6750               Allocate next image structure.
6751             */
6752             AcquireNextImage(image_info,image,exception);
6753 
6754             if (GetNextImageInList(image) == (Image *) NULL)
6755               return(DestroyImageList(image));
6756 
6757             image=SyncNextImageInList(image);
6758           }
6759         mng_info->image=image;
6760         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6761           GetBlobSize(image));
6762 
6763         if (status == MagickFalse)
6764           break;
6765 
6766         if (term_chunk_found)
6767           {
6768             image->start_loop=MagickTrue;
6769             term_chunk_found=MagickFalse;
6770           }
6771 
6772         else
6773             image->start_loop=MagickFalse;
6774 
6775         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6776           {
6777             image->delay=frame_delay;
6778             frame_delay=default_frame_delay;
6779           }
6780 
6781         else
6782           image->delay=0;
6783 
6784         image->page.width=mng_info->mng_width;
6785         image->page.height=mng_info->mng_height;
6786         image->page.x=mng_info->x_off[object_id];
6787         image->page.y=mng_info->y_off[object_id];
6788         image->iterations=mng_iterations;
6789 
6790         /*
6791           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6792         */
6793 
6794         if (logging != MagickFalse)
6795           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6796             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6797             type[2],type[3]);
6798 
6799         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6800 
6801         if (offset < 0)
6802           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6803       }
6804 
6805     mng_info->image=image;
6806     mng_info->mng_type=mng_type;
6807     mng_info->object_id=object_id;
6808 
6809     if (memcmp(type,mng_IHDR,4) == 0)
6810       image=ReadOnePNGImage(mng_info,image_info,exception);
6811 
6812 #if defined(JNG_SUPPORTED)
6813     else
6814       image=ReadOneJNGImage(mng_info,image_info,exception);
6815 #endif
6816 
6817     if (image == (Image *) NULL)
6818       {
6819         if (logging != MagickFalse)
6820           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6821             "exit ReadJNGImage() with error");
6822 
6823         return((Image *) NULL);
6824       }
6825 
6826     if (image->columns == 0 || image->rows == 0)
6827       {
6828         (void) CloseBlob(image);
6829         return(DestroyImageList(image));
6830       }
6831 
6832     mng_info->image=image;
6833 
6834     if (mng_type)
6835       {
6836         MngBox
6837           crop_box;
6838 
6839         if (((mng_info->magn_methx > 0) && (mng_info->magn_methx <= 5)) &&
6840             ((mng_info->magn_methy > 0) && (mng_info->magn_methy <= 5)))
6841           {
6842             png_uint_32
6843                magnified_height,
6844                magnified_width;
6845 
6846             if (logging != MagickFalse)
6847               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6848                 "  Processing MNG MAGN chunk");
6849 
6850             if (image->columns == 1)
6851               mng_info->magn_methx = 1;
6852             if (image->rows == 1)
6853               mng_info->magn_methy = 1;
6854             if (mng_info->magn_methx == 1)
6855               {
6856                 magnified_width=mng_info->magn_ml;
6857 
6858                 if (image->columns > 1)
6859                    magnified_width += mng_info->magn_mr;
6860 
6861                 if (image->columns > 2)
6862                    magnified_width += (png_uint_32)
6863                       ((image->columns-2)*(mng_info->magn_mx));
6864               }
6865 
6866             else
6867               {
6868                 magnified_width=(png_uint_32) image->columns;
6869 
6870                 if (image->columns > 1)
6871                    magnified_width += mng_info->magn_ml-1;
6872 
6873                 if (image->columns > 2)
6874                    magnified_width += mng_info->magn_mr-1;
6875 
6876                 if (image->columns > 3)
6877                    magnified_width += (png_uint_32)
6878                       ((image->columns-3)*(mng_info->magn_mx-1));
6879               }
6880 
6881             if (mng_info->magn_methy == 1)
6882               {
6883                 magnified_height=mng_info->magn_mt;
6884 
6885                 if (image->rows > 1)
6886                    magnified_height += mng_info->magn_mb;
6887 
6888                 if (image->rows > 2)
6889                    magnified_height += (png_uint_32)
6890                       ((image->rows-2)*(mng_info->magn_my));
6891               }
6892 
6893             else
6894               {
6895                 magnified_height=(png_uint_32) image->rows;
6896 
6897                 if (image->rows > 1)
6898                    magnified_height += mng_info->magn_mt-1;
6899 
6900                 if (image->rows > 2)
6901                    magnified_height += mng_info->magn_mb-1;
6902 
6903                 if (image->rows > 3)
6904                    magnified_height += (png_uint_32)
6905                       ((image->rows-3)*(mng_info->magn_my-1));
6906               }
6907 
6908             if (magnified_height > image->rows ||
6909                 magnified_width > image->columns)
6910               {
6911                 Image
6912                   *large_image;
6913 
6914                 int
6915                   yy;
6916 
6917                 Quantum
6918                   *next,
6919                   *prev;
6920 
6921                 png_uint_16
6922                   magn_methx,
6923                   magn_methy;
6924 
6925                 ssize_t
6926                   m,
6927                   y;
6928 
6929                 Quantum
6930                   *n,
6931                   *q;
6932 
6933                 ssize_t
6934                   x;
6935 
6936                 /* Allocate next image structure.  */
6937 
6938                 if (logging != MagickFalse)
6939                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6940                     "    Allocate magnified image");
6941 
6942                 AcquireNextImage(image_info,image,exception);
6943 
6944                 if (GetNextImageInList(image) == (Image *) NULL)
6945                   return(DestroyImageList(image));
6946 
6947                 large_image=SyncNextImageInList(image);
6948 
6949                 large_image->columns=magnified_width;
6950                 large_image->rows=magnified_height;
6951 
6952                 magn_methx=mng_info->magn_methx;
6953                 magn_methy=mng_info->magn_methy;
6954 
6955 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6956 #define QM unsigned short
6957                 if (magn_methx != 1 || magn_methy != 1)
6958                   {
6959                   /*
6960                      Scale pixels to unsigned shorts to prevent
6961                      overflow of intermediate values of interpolations
6962                   */
6963                      for (y=0; y < (ssize_t) image->rows; y++)
6964                      {
6965                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6966                           exception);
6967                        if (q == (Quantum *) NULL)
6968                          break;
6969                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6970                        {
6971                           SetPixelRed(image,ScaleQuantumToShort(
6972                             GetPixelRed(image,q)),q);
6973                           SetPixelGreen(image,ScaleQuantumToShort(
6974                             GetPixelGreen(image,q)),q);
6975                           SetPixelBlue(image,ScaleQuantumToShort(
6976                             GetPixelBlue(image,q)),q);
6977                           SetPixelAlpha(image,ScaleQuantumToShort(
6978                             GetPixelAlpha(image,q)),q);
6979                           q+=GetPixelChannels(image);
6980                        }
6981 
6982                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6983                          break;
6984                      }
6985                   }
6986 #else
6987 #define QM Quantum
6988 #endif
6989 
6990                 if (image->alpha_trait != UndefinedPixelTrait)
6991                    (void) SetImageBackgroundColor(large_image,exception);
6992 
6993                 else
6994                   {
6995                     large_image->background_color.alpha=OpaqueAlpha;
6996                     (void) SetImageBackgroundColor(large_image,exception);
6997 
6998                     if (magn_methx == 4)
6999                       magn_methx=2;
7000 
7001                     if (magn_methx == 5)
7002                       magn_methx=3;
7003 
7004                     if (magn_methy == 4)
7005                       magn_methy=2;
7006 
7007                     if (magn_methy == 5)
7008                       magn_methy=3;
7009                   }
7010 
7011                 /* magnify the rows into the right side of the large image */
7012 
7013                 if (logging != MagickFalse)
7014                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7015                     "    Magnify the rows to %.20g",
7016                     (double) large_image->rows);
7017                 m=(ssize_t) mng_info->magn_mt;
7018                 yy=0;
7019                 length=(size_t) GetPixelChannels(image)*image->columns;
7020                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
7021                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
7022 
7023                 if ((prev == (Quantum *) NULL) ||
7024                     (next == (Quantum *) NULL))
7025                   {
7026                     if (prev != (Quantum *) NULL)
7027                       prev=(Quantum *) RelinquishMagickMemory(prev);
7028                     if (next != (Quantum *) NULL)
7029                       next=(Quantum *) RelinquishMagickMemory(next);
7030                     image=DestroyImageList(image);
7031                     ThrowReaderException(ResourceLimitError,
7032                       "MemoryAllocationFailed");
7033                   }
7034 
7035                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
7036                 (void) memcpy(next,n,length);
7037 
7038                 for (y=0; y < (ssize_t) image->rows; y++)
7039                 {
7040                   if (y == 0)
7041                     m=(ssize_t) mng_info->magn_mt;
7042 
7043                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
7044                     m=(ssize_t) mng_info->magn_mb;
7045 
7046                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
7047                     m=(ssize_t) mng_info->magn_mb;
7048 
7049                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
7050                     m=1;
7051 
7052                   else
7053                     m=(ssize_t) mng_info->magn_my;
7054 
7055                   n=prev;
7056                   prev=next;
7057                   next=n;
7058 
7059                   if (y < (ssize_t) image->rows-1)
7060                     {
7061                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
7062                           exception);
7063                       (void) memcpy(next,n,length);
7064                     }
7065 
7066                   for (i=0; i < m; i++, yy++)
7067                   {
7068                     Quantum
7069                       *pixels;
7070 
7071                     assert(yy < (ssize_t) large_image->rows);
7072                     pixels=prev;
7073                     n=next;
7074                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
7075                       1,exception);
7076                     if (q == (Quantum *) NULL)
7077                       break;
7078                     q+=(large_image->columns-image->columns)*
7079                       GetPixelChannels(large_image);
7080 
7081                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
7082                     {
7083                       /* To do: get color as function of indexes[x] */
7084                       /*
7085                       if (image->storage_class == PseudoClass)
7086                         {
7087                         }
7088                       */
7089 
7090                       if (magn_methy <= 1)
7091                         {
7092                           /* replicate previous */
7093                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
7094                           SetPixelGreen(large_image,GetPixelGreen(image,
7095                              pixels),q);
7096                           SetPixelBlue(large_image,GetPixelBlue(image,
7097                              pixels),q);
7098                           SetPixelAlpha(large_image,GetPixelAlpha(image,
7099                              pixels),q);
7100                         }
7101 
7102                       else if (magn_methy == 2 || magn_methy == 4)
7103                         {
7104                           if (i == 0)
7105                             {
7106                               SetPixelRed(large_image,GetPixelRed(image,
7107                                  pixels),q);
7108                               SetPixelGreen(large_image,GetPixelGreen(image,
7109                                  pixels),q);
7110                               SetPixelBlue(large_image,GetPixelBlue(image,
7111                                  pixels),q);
7112                               SetPixelAlpha(large_image,GetPixelAlpha(image,
7113                                  pixels),q);
7114                             }
7115 
7116                           else
7117                             {
7118                               /* Interpolate */
7119                               SetPixelRed(large_image,((QM) (((ssize_t)
7120                                  (2*i*(GetPixelRed(image,n)
7121                                  -GetPixelRed(image,pixels)+m))/
7122                                  ((ssize_t) (m*2))
7123                                  +GetPixelRed(image,pixels)))),q);
7124                               SetPixelGreen(large_image,((QM) (((ssize_t)
7125                                  (2*i*(GetPixelGreen(image,n)
7126                                  -GetPixelGreen(image,pixels)+m))/
7127                                  ((ssize_t) (m*2))
7128                                  +GetPixelGreen(image,pixels)))),q);
7129                               SetPixelBlue(large_image,((QM) (((ssize_t)
7130                                  (2*i*(GetPixelBlue(image,n)
7131                                  -GetPixelBlue(image,pixels)+m))/
7132                                  ((ssize_t) (m*2))
7133                                  +GetPixelBlue(image,pixels)))),q);
7134 
7135                               if (image->alpha_trait != UndefinedPixelTrait)
7136                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
7137                                     (2*i*(GetPixelAlpha(image,n)
7138                                     -GetPixelAlpha(image,pixels)+m))
7139                                     /((ssize_t) (m*2))+
7140                                    GetPixelAlpha(image,pixels)))),q);
7141                             }
7142 
7143                           if (magn_methy == 4)
7144                             {
7145                               /* Replicate nearest */
7146                               if (i <= ((m+1) << 1))
7147                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7148                                     pixels),q);
7149                               else
7150                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7151                                     n),q);
7152                             }
7153                         }
7154 
7155                       else /* if (magn_methy == 3 || magn_methy == 5) */
7156                         {
7157                           /* Replicate nearest */
7158                           if (i <= ((m+1) << 1))
7159                           {
7160                              SetPixelRed(large_image,GetPixelRed(image,
7161                                     pixels),q);
7162                              SetPixelGreen(large_image,GetPixelGreen(image,
7163                                     pixels),q);
7164                              SetPixelBlue(large_image,GetPixelBlue(image,
7165                                     pixels),q);
7166                              SetPixelAlpha(large_image,GetPixelAlpha(image,
7167                                     pixels),q);
7168                           }
7169 
7170                           else
7171                           {
7172                              SetPixelRed(large_image,GetPixelRed(image,n),q);
7173                              SetPixelGreen(large_image,GetPixelGreen(image,n),
7174                                     q);
7175                              SetPixelBlue(large_image,GetPixelBlue(image,n),
7176                                     q);
7177                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7178                                     q);
7179                           }
7180 
7181                           if (magn_methy == 5)
7182                             {
7183                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7184                                  (GetPixelAlpha(image,n)
7185                                  -GetPixelAlpha(image,pixels))
7186                                  +m))/((ssize_t) (m*2))
7187                                  +GetPixelAlpha(image,pixels)),q);
7188                             }
7189                         }
7190                       n+=GetPixelChannels(image);
7191                       q+=GetPixelChannels(large_image);
7192                       pixels+=GetPixelChannels(image);
7193                     } /* x */
7194 
7195                     if (SyncAuthenticPixels(large_image,exception) == 0)
7196                       break;
7197 
7198                   } /* i */
7199                 } /* y */
7200 
7201                 prev=(Quantum *) RelinquishMagickMemory(prev);
7202                 next=(Quantum *) RelinquishMagickMemory(next);
7203 
7204                 length=image->columns;
7205 
7206                 if (logging != MagickFalse)
7207                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7208                     "    Delete original image");
7209 
7210                 DeleteImageFromList(&image);
7211 
7212                 image=large_image;
7213 
7214                 mng_info->image=image;
7215 
7216                 /* magnify the columns */
7217                 if (logging != MagickFalse)
7218                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7219                     "    Magnify the columns to %.20g",
7220                     (double) image->columns);
7221 
7222                 for (y=0; y < (ssize_t) image->rows; y++)
7223                 {
7224                   Quantum
7225                     *pixels;
7226 
7227                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7228                   if (q == (Quantum *) NULL)
7229                     break;
7230                   pixels=q+(image->columns-length)*GetPixelChannels(image);
7231                   n=pixels+GetPixelChannels(image);
7232 
7233                   for (x=(ssize_t) (image->columns-length);
7234                     x < (ssize_t) image->columns; x++)
7235                   {
7236                     /* To do: Rewrite using Get/Set***PixelChannel() */
7237 
7238                     if (x == (ssize_t) (image->columns-length))
7239                       m=(ssize_t) mng_info->magn_ml;
7240 
7241                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7242                       m=(ssize_t) mng_info->magn_mr;
7243 
7244                     else if (magn_methx <= 1 &&
7245                         x == (ssize_t) image->columns-1)
7246                       m=(ssize_t) mng_info->magn_mr;
7247 
7248                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7249                       m=1;
7250 
7251                     else
7252                       m=(ssize_t) mng_info->magn_mx;
7253 
7254                     for (i=0; i < m; i++)
7255                     {
7256                       if (magn_methx <= 1)
7257                         {
7258                           /* replicate previous */
7259                           SetPixelRed(image,GetPixelRed(image,pixels),q);
7260                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7261                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7262                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7263                         }
7264 
7265                       else if (magn_methx == 2 || magn_methx == 4)
7266                         {
7267                           if (i == 0)
7268                           {
7269                             SetPixelRed(image,GetPixelRed(image,pixels),q);
7270                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7271                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7272                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7273                           }
7274 
7275                           /* To do: Rewrite using Get/Set***PixelChannel() */
7276                           else
7277                             {
7278                               /* Interpolate */
7279                               SetPixelRed(image,(QM) ((2*i*(
7280                                  GetPixelRed(image,n)
7281                                  -GetPixelRed(image,pixels))+m)
7282                                  /((ssize_t) (m*2))+
7283                                  GetPixelRed(image,pixels)),q);
7284 
7285                               SetPixelGreen(image,(QM) ((2*i*(
7286                                  GetPixelGreen(image,n)
7287                                  -GetPixelGreen(image,pixels))+m)
7288                                  /((ssize_t) (m*2))+
7289                                  GetPixelGreen(image,pixels)),q);
7290 
7291                               SetPixelBlue(image,(QM) ((2*i*(
7292                                  GetPixelBlue(image,n)
7293                                  -GetPixelBlue(image,pixels))+m)
7294                                  /((ssize_t) (m*2))+
7295                                  GetPixelBlue(image,pixels)),q);
7296                               if (image->alpha_trait != UndefinedPixelTrait)
7297                                  SetPixelAlpha(image,(QM) ((2*i*(
7298                                    GetPixelAlpha(image,n)
7299                                    -GetPixelAlpha(image,pixels))+m)
7300                                    /((ssize_t) (m*2))+
7301                                    GetPixelAlpha(image,pixels)),q);
7302                             }
7303 
7304                           if (magn_methx == 4)
7305                             {
7306                               /* Replicate nearest */
7307                               if (i <= ((m+1) << 1))
7308                               {
7309                                  SetPixelAlpha(image,
7310                                    GetPixelAlpha(image,pixels)+0,q);
7311                               }
7312                               else
7313                               {
7314                                  SetPixelAlpha(image,
7315                                    GetPixelAlpha(image,n)+0,q);
7316                               }
7317                             }
7318                         }
7319 
7320                       else /* if (magn_methx == 3 || magn_methx == 5) */
7321                         {
7322                           /* Replicate nearest */
7323                           if (i <= ((m+1) << 1))
7324                           {
7325                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7326                              SetPixelGreen(image,GetPixelGreen(image,
7327                                  pixels),q);
7328                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7329                              SetPixelAlpha(image,GetPixelAlpha(image,
7330                                  pixels),q);
7331                           }
7332 
7333                           else
7334                           {
7335                              SetPixelRed(image,GetPixelRed(image,n),q);
7336                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7337                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7338                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7339                           }
7340 
7341                           if (magn_methx == 5)
7342                             {
7343                               /* Interpolate */
7344                               SetPixelAlpha(image,
7345                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7346                                  -GetPixelAlpha(image,pixels))+m)/
7347                                  ((ssize_t) (m*2))
7348                                  +GetPixelAlpha(image,pixels)),q);
7349                             }
7350                         }
7351                       q+=GetPixelChannels(image);
7352                     }
7353                     n+=GetPixelChannels(image);
7354                   }
7355 
7356                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7357                     break;
7358                 }
7359 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7360               if (magn_methx != 1 || magn_methy != 1)
7361                 {
7362                 /*
7363                    Rescale pixels to Quantum
7364                 */
7365                    for (y=0; y < (ssize_t) image->rows; y++)
7366                    {
7367                      q=GetAuthenticPixels(image,0,y,image->columns,1,
7368                        exception);
7369                      if (q == (Quantum *) NULL)
7370                        break;
7371 
7372                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7373                      {
7374                         SetPixelRed(image,ScaleShortToQuantum(
7375                           GetPixelRed(image,q)),q);
7376                         SetPixelGreen(image,ScaleShortToQuantum(
7377                           GetPixelGreen(image,q)),q);
7378                         SetPixelBlue(image,ScaleShortToQuantum(
7379                           GetPixelBlue(image,q)),q);
7380                         SetPixelAlpha(image,ScaleShortToQuantum(
7381                           GetPixelAlpha(image,q)),q);
7382                         q+=GetPixelChannels(image);
7383                      }
7384 
7385                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7386                        break;
7387                    }
7388                 }
7389 #endif
7390                 if (logging != MagickFalse)
7391                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7392                     "  Finished MAGN processing");
7393               }
7394           }
7395 
7396         /*
7397           Crop_box is with respect to the upper left corner of the MNG.
7398         */
7399         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7400         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7401         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7402         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7403         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7404         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7405         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7406         if ((crop_box.left != (mng_info->image_box.left
7407             +mng_info->x_off[object_id])) ||
7408             (crop_box.right != (mng_info->image_box.right
7409             +mng_info->x_off[object_id])) ||
7410             (crop_box.top != (mng_info->image_box.top
7411             +mng_info->y_off[object_id])) ||
7412             (crop_box.bottom != (mng_info->image_box.bottom
7413             +mng_info->y_off[object_id])))
7414           {
7415             if (logging != MagickFalse)
7416               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7417                 "  Crop the PNG image");
7418 
7419             if ((crop_box.left < crop_box.right) &&
7420                 (crop_box.top < crop_box.bottom))
7421               {
7422                 Image
7423                   *im;
7424 
7425                 RectangleInfo
7426                   crop_info;
7427 
7428                 /*
7429                   Crop_info is with respect to the upper left corner of
7430                   the image.
7431                 */
7432                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7433                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7434                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7435                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7436                 image->page.width=image->columns;
7437                 image->page.height=image->rows;
7438                 image->page.x=0;
7439                 image->page.y=0;
7440                 im=CropImage(image,&crop_info,exception);
7441 
7442                 if (im != (Image *) NULL)
7443                   {
7444                     image->columns=im->columns;
7445                     image->rows=im->rows;
7446                     im=DestroyImage(im);
7447                     image->page.width=image->columns;
7448                     image->page.height=image->rows;
7449                     image->page.x=crop_box.left;
7450                     image->page.y=crop_box.top;
7451                   }
7452               }
7453 
7454             else
7455               {
7456                 /*
7457                   No pixels in crop area.  The MNG spec still requires
7458                   a layer, though, so make a single transparent pixel in
7459                   the top left corner.
7460                 */
7461                 image->columns=1;
7462                 image->rows=1;
7463                 image->colors=2;
7464                 (void) SetImageBackgroundColor(image,exception);
7465                 image->page.width=1;
7466                 image->page.height=1;
7467                 image->page.x=0;
7468                 image->page.y=0;
7469               }
7470           }
7471 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7472         image=mng_info->image;
7473 #endif
7474       }
7475 
7476 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7477       /* PNG does not handle depths greater than 16 so reduce it even
7478        * if lossy.
7479        */
7480       if (image->depth > 16)
7481          image->depth=16;
7482 #endif
7483 
7484 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7485       if (image->depth > 8)
7486         {
7487           /* To do: fill low byte properly */
7488           image->depth=16;
7489         }
7490 
7491       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7492          image->depth = 8;
7493 #endif
7494 
7495       if (image_info->number_scenes != 0)
7496         {
7497           if (mng_info->scenes_found >
7498              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7499             break;
7500         }
7501 
7502       if (logging != MagickFalse)
7503         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7504           "  Finished reading image datastream.");
7505 
7506   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7507 
7508   (void) CloseBlob(image);
7509 
7510   if (logging != MagickFalse)
7511     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7512       "  Finished reading all image datastreams.");
7513 
7514 #if defined(MNG_INSERT_LAYERS)
7515   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7516        (mng_info->mng_height))
7517     {
7518       /*
7519         Insert a background layer if nothing else was found.
7520       */
7521       if (logging != MagickFalse)
7522         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7523           "  No images found.  Inserting a background layer.");
7524 
7525       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7526         {
7527           /*
7528             Allocate next image structure.
7529           */
7530           AcquireNextImage(image_info,image,exception);
7531           if (GetNextImageInList(image) == (Image *) NULL)
7532             {
7533               if (logging != MagickFalse)
7534                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7535                   "  Allocation failed, returning NULL.");
7536 
7537               return(DestroyImageList(image));;
7538             }
7539           image=SyncNextImageInList(image);
7540         }
7541       image->columns=mng_info->mng_width;
7542       image->rows=mng_info->mng_height;
7543       image->page.width=mng_info->mng_width;
7544       image->page.height=mng_info->mng_height;
7545       image->page.x=0;
7546       image->page.y=0;
7547       image->background_color=mng_background_color;
7548       image->alpha_trait=UndefinedPixelTrait;
7549 
7550       if (image_info->ping == MagickFalse)
7551         (void) SetImageBackgroundColor(image,exception);
7552 
7553       mng_info->image_found++;
7554     }
7555 #endif
7556   image->iterations=mng_iterations;
7557 
7558   if (mng_iterations == 1)
7559     image->start_loop=MagickTrue;
7560 
7561   while (GetPreviousImageInList(image) != (Image *) NULL)
7562   {
7563     image_count++;
7564     if (image_count > 10*mng_info->image_found)
7565       {
7566         if (logging != MagickFalse)
7567           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7568 
7569         (void) ThrowMagickException(exception,GetMagickModule(),
7570           CoderError,"Linked list is corrupted, beginning of list not found",
7571           "`%s'",image_info->filename);
7572 
7573         return(DestroyImageList(image));
7574       }
7575 
7576     image=GetPreviousImageInList(image);
7577 
7578     if (GetNextImageInList(image) == (Image *) NULL)
7579       {
7580         if (logging != MagickFalse)
7581           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7582 
7583         (void) ThrowMagickException(exception,GetMagickModule(),
7584           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7585           image_info->filename);
7586       }
7587   }
7588 
7589   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7590              GetNextImageInList(image) ==
7591      (Image *) NULL)
7592     {
7593       if (logging != MagickFalse)
7594         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7595             "  First image null");
7596 
7597       (void) ThrowMagickException(exception,GetMagickModule(),
7598         CoderError,"image->next for first image is NULL but shouldn't be.",
7599         "`%s'",image_info->filename);
7600     }
7601 
7602   if (mng_info->image_found == 0)
7603     {
7604       if (logging != MagickFalse)
7605         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7606           "  No visible images found.");
7607 
7608       (void) ThrowMagickException(exception,GetMagickModule(),
7609         CoderError,"No visible images in file","`%s'",image_info->filename);
7610 
7611       return(DestroyImageList(image));
7612     }
7613 
7614   if (mng_info->ticks_per_second)
7615     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7616             final_delay/mng_info->ticks_per_second;
7617 
7618   else
7619     image->start_loop=MagickTrue;
7620 
7621   /* Find final nonzero image delay */
7622   final_image_delay=0;
7623 
7624   while (GetNextImageInList(image) != (Image *) NULL)
7625     {
7626       if (image->delay)
7627         final_image_delay=image->delay;
7628 
7629       image=GetNextImageInList(image);
7630     }
7631 
7632   if (final_delay < final_image_delay)
7633     final_delay=final_image_delay;
7634 
7635   image->delay=final_delay;
7636 
7637   if (logging != MagickFalse)
7638       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7639         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7640         (double) final_delay);
7641 
7642   if (logging != MagickFalse)
7643     {
7644       int
7645         scene;
7646 
7647       scene=0;
7648       image=GetFirstImageInList(image);
7649 
7650       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7651         "  Before coalesce:");
7652 
7653       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7654         "    scene 0 delay=%.20g",(double) image->delay);
7655 
7656       while (GetNextImageInList(image) != (Image *) NULL)
7657       {
7658         image=GetNextImageInList(image);
7659         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7660           "    scene %.20g delay=%.20g",(double) scene++,
7661           (double) image->delay);
7662       }
7663     }
7664 
7665   image=GetFirstImageInList(image);
7666 #ifdef MNG_COALESCE_LAYERS
7667   if (insert_layers && image->next)
7668     {
7669       Image
7670         *next_image,
7671         *next;
7672 
7673       size_t
7674         scene;
7675 
7676       if (logging != MagickFalse)
7677         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7678           "  Coalesce Images");
7679 
7680       scene=image->scene;
7681       next_image=CoalesceImages(image,exception);
7682       image=DestroyImageList(image);
7683       if (next_image == (Image *) NULL)
7684         return((Image *) NULL);
7685       image=next_image;
7686 
7687       for (next=image; next != (Image *) NULL; next=next_image)
7688       {
7689          next->page.width=mng_info->mng_width;
7690          next->page.height=mng_info->mng_height;
7691          next->page.x=0;
7692          next->page.y=0;
7693          next->scene=scene++;
7694          next_image=GetNextImageInList(next);
7695 
7696          if (next_image == (Image *) NULL)
7697            break;
7698 
7699          if (next->delay == 0)
7700            {
7701              scene--;
7702              next_image->previous=GetPreviousImageInList(next);
7703              if (GetPreviousImageInList(next) == (Image *) NULL)
7704                image=next_image;
7705              else
7706                next->previous->next=next_image;
7707              next=DestroyImage(next);
7708            }
7709       }
7710     }
7711 #endif
7712 
7713   while (GetNextImageInList(image) != (Image *) NULL)
7714       image=GetNextImageInList(image);
7715 
7716   image->dispose=BackgroundDispose;
7717 
7718   if (logging != MagickFalse)
7719     {
7720       int
7721         scene;
7722 
7723       scene=0;
7724       image=GetFirstImageInList(image);
7725 
7726       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7727         "  After coalesce:");
7728 
7729       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7730         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7731         (double) image->dispose);
7732 
7733       while (GetNextImageInList(image) != (Image *) NULL)
7734       {
7735         image=GetNextImageInList(image);
7736 
7737         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7738           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7739           (double) image->delay,(double) image->dispose);
7740       }
7741    }
7742 
7743   if (logging != MagickFalse)
7744     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7745       "  exit ReadOneMNGImage();");
7746 
7747   return(image);
7748 }
7749 
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7750 static Image *ReadMNGImage(const ImageInfo *image_info,
7751      ExceptionInfo *exception)
7752 {
7753   Image
7754     *image;
7755 
7756   MagickBooleanType
7757     logging,
7758     status;
7759 
7760   MngInfo
7761     *mng_info;
7762 
7763   /* Open image file.  */
7764 
7765   assert(image_info != (const ImageInfo *) NULL);
7766   assert(image_info->signature == MagickCoreSignature);
7767   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7768      image_info->filename);
7769   assert(exception != (ExceptionInfo *) NULL);
7770   assert(exception->signature == MagickCoreSignature);
7771   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7772   image=AcquireImage(image_info,exception);
7773   mng_info=(MngInfo *) NULL;
7774   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7775 
7776   if (status == MagickFalse)
7777     return(DestroyImageList(image));
7778 
7779   /* Allocate a MngInfo structure.  */
7780 
7781   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7782 
7783   if (mng_info == (MngInfo *) NULL)
7784     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7785 
7786   /* Initialize members of the MngInfo structure.  */
7787 
7788   (void) memset(mng_info,0,sizeof(MngInfo));
7789   mng_info->image=image;
7790   image=ReadOneMNGImage(mng_info,image_info,exception);
7791   mng_info=MngInfoFreeStruct(mng_info);
7792 
7793   if (image == (Image *) NULL)
7794     {
7795       if (logging != MagickFalse)
7796         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7797           "exit ReadMNGImage() with error");
7798 
7799       return((Image *) NULL);
7800     }
7801   (void) CloseBlob(image);
7802 
7803   if (logging != MagickFalse)
7804     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7805 
7806   return(GetFirstImageInList(image));
7807 }
7808 #else /* PNG_LIBPNG_VER > 10011 */
ReadPNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7809 static Image *ReadPNGImage(const ImageInfo *image_info,
7810    ExceptionInfo *exception)
7811 {
7812   printf("Your PNG library is too old: You have libpng-%s\n",
7813      PNG_LIBPNG_VER_STRING);
7814 
7815   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7816     "PNG library is too old","`%s'",image_info->filename);
7817 
7818   return(Image *) NULL;
7819 }
7820 
ReadMNGImage(const ImageInfo * image_info,ExceptionInfo * exception)7821 static Image *ReadMNGImage(const ImageInfo *image_info,
7822    ExceptionInfo *exception)
7823 {
7824   return(ReadPNGImage(image_info,exception));
7825 }
7826 #endif /* PNG_LIBPNG_VER > 10011 */
7827 #endif
7828 
7829 /*
7830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7831 %                                                                             %
7832 %                                                                             %
7833 %                                                                             %
7834 %   R e g i s t e r P N G I m a g e                                           %
7835 %                                                                             %
7836 %                                                                             %
7837 %                                                                             %
7838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7839 %
7840 %  RegisterPNGImage() adds properties for the PNG image format to
7841 %  the list of supported formats.  The properties include the image format
7842 %  tag, a method to read and/or write the format, whether the format
7843 %  supports the saving of more than one frame to the same file or blob,
7844 %  whether the format supports native in-memory I/O, and a brief
7845 %  description of the format.
7846 %
7847 %  The format of the RegisterPNGImage method is:
7848 %
7849 %      size_t RegisterPNGImage(void)
7850 %
7851 */
RegisterPNGImage(void)7852 ModuleExport size_t RegisterPNGImage(void)
7853 {
7854   char
7855     version[MagickPathExtent];
7856 
7857   MagickInfo
7858     *entry;
7859 
7860   static const char
7861     PNGNote[] =
7862     {
7863       "See http://www.libpng.org/ for details about the PNG format."
7864     },
7865 
7866     JNGNote[] =
7867     {
7868       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7869       "format."
7870     },
7871 
7872     MNGNote[] =
7873     {
7874       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7875       "format."
7876     };
7877 
7878   *version='\0';
7879 
7880 #if defined(PNG_LIBPNG_VER_STRING)
7881   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7882   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7883    MagickPathExtent);
7884 
7885   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7886     {
7887       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7888       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7889             MagickPathExtent);
7890     }
7891 #endif
7892 
7893   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7894   entry->flags|=CoderDecoderSeekableStreamFlag;
7895 
7896 #if defined(MAGICKCORE_PNG_DELEGATE)
7897   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7898   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7899 #endif
7900 
7901   entry->magick=(IsImageFormatHandler *) IsMNG;
7902 
7903   if (*version != '\0')
7904     entry->version=ConstantString(version);
7905 
7906   entry->mime_type=ConstantString("video/x-mng");
7907   entry->note=ConstantString(MNGNote);
7908   (void) RegisterMagickInfo(entry);
7909 
7910   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7911 
7912 #if defined(MAGICKCORE_PNG_DELEGATE)
7913   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7914   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7915 #endif
7916 
7917   entry->magick=(IsImageFormatHandler *) IsPNG;
7918   entry->flags|=CoderDecoderSeekableStreamFlag;
7919   entry->flags^=CoderAdjoinFlag;
7920   entry->mime_type=ConstantString("image/png");
7921 
7922   if (*version != '\0')
7923     entry->version=ConstantString(version);
7924 
7925   entry->note=ConstantString(PNGNote);
7926   (void) RegisterMagickInfo(entry);
7927 
7928   entry=AcquireMagickInfo("PNG","PNG8",
7929     "8-bit indexed with optional binary transparency");
7930 
7931 #if defined(MAGICKCORE_PNG_DELEGATE)
7932   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7933   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7934 #endif
7935 
7936   entry->magick=(IsImageFormatHandler *) IsPNG;
7937   entry->flags|=CoderDecoderSeekableStreamFlag;
7938   entry->flags^=CoderAdjoinFlag;
7939   entry->mime_type=ConstantString("image/png");
7940   (void) RegisterMagickInfo(entry);
7941 
7942   entry=AcquireMagickInfo("PNG","PNG24",
7943     "opaque or binary transparent 24-bit RGB");
7944   *version='\0';
7945 
7946 #if defined(ZLIB_VERSION)
7947   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7948   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7949 
7950   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7951     {
7952       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7953       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7954     }
7955 #endif
7956 
7957   if (*version != '\0')
7958     entry->version=ConstantString(version);
7959 
7960 #if defined(MAGICKCORE_PNG_DELEGATE)
7961   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7962   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7963 #endif
7964 
7965   entry->magick=(IsImageFormatHandler *) IsPNG;
7966   entry->flags|=CoderDecoderSeekableStreamFlag;
7967   entry->flags^=CoderAdjoinFlag;
7968   entry->mime_type=ConstantString("image/png");
7969   (void) RegisterMagickInfo(entry);
7970 
7971   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7972 
7973 #if defined(MAGICKCORE_PNG_DELEGATE)
7974   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7975   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7976 #endif
7977 
7978   entry->magick=(IsImageFormatHandler *) IsPNG;
7979   entry->flags|=CoderDecoderSeekableStreamFlag;
7980   entry->flags^=CoderAdjoinFlag;
7981   entry->mime_type=ConstantString("image/png");
7982   (void) RegisterMagickInfo(entry);
7983 
7984   entry=AcquireMagickInfo("PNG","PNG48",
7985     "opaque or binary transparent 48-bit RGB");
7986 
7987 #if defined(MAGICKCORE_PNG_DELEGATE)
7988   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7989   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7990 #endif
7991 
7992   entry->magick=(IsImageFormatHandler *) IsPNG;
7993   entry->flags|=CoderDecoderSeekableStreamFlag;
7994   entry->flags^=CoderAdjoinFlag;
7995   entry->mime_type=ConstantString("image/png");
7996   (void) RegisterMagickInfo(entry);
7997 
7998   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7999 
8000 #if defined(MAGICKCORE_PNG_DELEGATE)
8001   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
8002   entry->encoder=(EncodeImageHandler *) WritePNGImage;
8003 #endif
8004 
8005   entry->magick=(IsImageFormatHandler *) IsPNG;
8006   entry->flags|=CoderDecoderSeekableStreamFlag;
8007   entry->flags^=CoderAdjoinFlag;
8008   entry->mime_type=ConstantString("image/png");
8009   (void) RegisterMagickInfo(entry);
8010 
8011   entry=AcquireMagickInfo("PNG","PNG00",
8012     "PNG inheriting bit-depth, color-type from original, if possible");
8013 
8014 #if defined(MAGICKCORE_PNG_DELEGATE)
8015   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
8016   entry->encoder=(EncodeImageHandler *) WritePNGImage;
8017 #endif
8018 
8019   entry->magick=(IsImageFormatHandler *) IsPNG;
8020   entry->flags|=CoderDecoderSeekableStreamFlag;
8021   entry->flags^=CoderAdjoinFlag;
8022   entry->mime_type=ConstantString("image/png");
8023   (void) RegisterMagickInfo(entry);
8024 
8025   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
8026 
8027 #if defined(JNG_SUPPORTED)
8028 #if defined(MAGICKCORE_PNG_DELEGATE)
8029   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
8030   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
8031 #endif
8032 #endif
8033 
8034   entry->magick=(IsImageFormatHandler *) IsJNG;
8035   entry->flags|=CoderDecoderSeekableStreamFlag;
8036   entry->flags^=CoderAdjoinFlag;
8037   entry->mime_type=ConstantString("image/x-jng");
8038   entry->note=ConstantString(JNGNote);
8039   (void) RegisterMagickInfo(entry);
8040 
8041 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8042   ping_semaphore=AcquireSemaphoreInfo();
8043 #endif
8044 
8045   return(MagickImageCoderSignature);
8046 }
8047 
8048 /*
8049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8050 %                                                                             %
8051 %                                                                             %
8052 %                                                                             %
8053 %   U n r e g i s t e r P N G I m a g e                                       %
8054 %                                                                             %
8055 %                                                                             %
8056 %                                                                             %
8057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8058 %
8059 %  UnregisterPNGImage() removes format registrations made by the
8060 %  PNG module from the list of supported formats.
8061 %
8062 %  The format of the UnregisterPNGImage method is:
8063 %
8064 %      UnregisterPNGImage(void)
8065 %
8066 */
UnregisterPNGImage(void)8067 ModuleExport void UnregisterPNGImage(void)
8068 {
8069   (void) UnregisterMagickInfo("MNG");
8070   (void) UnregisterMagickInfo("PNG");
8071   (void) UnregisterMagickInfo("PNG8");
8072   (void) UnregisterMagickInfo("PNG24");
8073   (void) UnregisterMagickInfo("PNG32");
8074   (void) UnregisterMagickInfo("PNG48");
8075   (void) UnregisterMagickInfo("PNG64");
8076   (void) UnregisterMagickInfo("PNG00");
8077   (void) UnregisterMagickInfo("JNG");
8078 
8079 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
8080   if (ping_semaphore != (SemaphoreInfo *) NULL)
8081     RelinquishSemaphoreInfo(&ping_semaphore);
8082 #endif
8083 }
8084 
8085 #if defined(MAGICKCORE_PNG_DELEGATE)
8086 #if PNG_LIBPNG_VER > 10011
8087 /*
8088 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8089 %                                                                             %
8090 %                                                                             %
8091 %                                                                             %
8092 %   W r i t e M N G I m a g e                                                 %
8093 %                                                                             %
8094 %                                                                             %
8095 %                                                                             %
8096 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
8097 %
8098 %  WriteMNGImage() writes an image in the Portable Network Graphics
8099 %  Group's "Multiple-image Network Graphics" encoded image format.
8100 %
8101 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
8102 %
8103 %  The format of the WriteMNGImage method is:
8104 %
8105 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
8106 %        Image *image,ExceptionInfo *exception)
8107 %
8108 %  A description of each parameter follows.
8109 %
8110 %    o image_info: the image info.
8111 %
8112 %    o image:  The image.
8113 %
8114 %    o exception: return any errors or warnings in this structure.
8115 %
8116 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
8117 %    "To do" under ReadPNGImage):
8118 %
8119 %    Preserve all unknown and not-yet-handled known chunks found in input
8120 %    PNG file and copy them  into output PNG files according to the PNG
8121 %    copying rules.
8122 %
8123 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
8124 %
8125 %    Improve selection of color type (use indexed-color or indexed-color
8126 %    with tRNS when 256 or fewer unique RGBA values are present).
8127 %
8128 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8129 %    This will be complicated if we limit ourselves to generating MNG-LC
8130 %    files.  For now we ignore disposal method 3 and simply overlay the next
8131 %    image on it.
8132 %
8133 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
8134 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
8135 %    [mostly done 15 June 1999 but still need to take care of tRNS]
8136 %
8137 %    Check for identical sRGB and replace with a global sRGB (and remove
8138 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8139 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8140 %    local gAMA/cHRM with local sRGB if appropriate).
8141 %
8142 %    Check for identical sBIT chunks and write global ones.
8143 %
8144 %    Provide option to skip writing the signature tEXt chunks.
8145 %
8146 %    Use signatures to detect identical objects and reuse the first
8147 %    instance of such objects instead of writing duplicate objects.
8148 %
8149 %    Use a smaller-than-32k value of compression window size when
8150 %    appropriate.
8151 %
8152 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
8153 %    ancillary text chunks and save profiles.
8154 %
8155 %    Provide an option to force LC files (to ensure exact framing rate)
8156 %    instead of VLC.
8157 %
8158 %    Provide an option to force VLC files instead of LC, even when offsets
8159 %    are present.  This will involve expanding the embedded images with a
8160 %    transparent region at the top and/or left.
8161 */
8162 
8163 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,ExceptionInfo * exception)8164 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8165   png_info *ping_info, unsigned char *profile_type, unsigned char
8166   *profile_description, unsigned char *profile_data, png_uint_32 length,
8167   ExceptionInfo *exception)
8168 {
8169    png_charp
8170      dp;
8171 
8172    png_textp
8173      text;
8174 
8175    png_uint_32
8176      allocated_length,
8177      description_length;
8178 
8179    ssize_t
8180      i;
8181 
8182    unsigned char
8183      hex[16] =
8184        { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' },
8185      *sp;
8186 
8187    if (length > 1)
8188      {
8189        if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8190           return;
8191      }
8192    if (image_info->verbose != MagickFalse)
8193      {
8194        (void) printf("writing raw profile: type=%s, length=%.20g\n",
8195          (char *) profile_type, (double) length);
8196      }
8197    description_length=(png_uint_32) strlen((const char *) profile_description);
8198    allocated_length=(png_uint_32) (2*length+(length >> 5)+description_length+
8199      20);
8200    if (allocated_length < length)
8201      {
8202        (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8203          "maximum profile length exceeded","`%s'",image_info->filename);
8204        return;
8205      }
8206 #if PNG_LIBPNG_VER >= 10400
8207    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8208 #else
8209    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8210 #endif
8211 #if PNG_LIBPNG_VER >= 10400
8212    text[0].text=(png_charp) png_malloc(ping,(png_alloc_size_t)
8213      allocated_length);
8214    text[0].key=(png_charp) png_malloc(ping,(png_alloc_size_t) 80);
8215 #else
8216    text[0].text=(png_charp) png_malloc(ping,(png_size_t) allocated_length);
8217    text[0].key=(png_charp) png_malloc(ping,(png_size_t) 80);
8218 #endif
8219    text[0].key[0]='\0';
8220    (void) ConcatenateMagickString(text[0].key,"Raw profile type ",
8221      MagickPathExtent);
8222    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8223    sp=profile_data;
8224    dp=text[0].text;
8225    *dp++='\n';
8226    (void) CopyMagickString(dp,(const char *) profile_description,
8227      allocated_length);
8228    dp+=description_length;
8229    *dp++='\n';
8230    (void) FormatLocaleString(dp,allocated_length-
8231      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8232    dp+=8;
8233 
8234    for (i=0; i < (ssize_t) length; i++)
8235    {
8236      if (i%36 == 0)
8237        *dp++='\n';
8238      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8239      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8240    }
8241 
8242    *dp++='\n';
8243    *dp='\0';
8244    text[0].text_length=(png_size_t) (dp-text[0].text);
8245    text[0].compression=image_info->compression == NoCompression ||
8246      (image_info->compression == UndefinedCompression &&
8247      text[0].text_length < 128) ? -1 : 0;
8248 
8249    if (text[0].text_length <= allocated_length)
8250      png_set_text(ping,ping_info,text,1);
8251 
8252    png_free(ping,text[0].text);
8253    png_free(ping,text[0].key);
8254    png_free(ping,text);
8255 }
8256 
IsColorEqual(const Image * image,const Quantum * p,const PixelInfo * q)8257 static inline MagickBooleanType IsColorEqual(const Image *image,
8258   const Quantum *p, const PixelInfo *q)
8259 {
8260   MagickRealType
8261     blue,
8262     green,
8263     red;
8264 
8265   red=(MagickRealType) GetPixelRed(image,p);
8266   green=(MagickRealType) GetPixelGreen(image,p);
8267   blue=(MagickRealType) GetPixelBlue(image,p);
8268   if ((AbsolutePixelValue(red-q->red) < MagickEpsilon) &&
8269       (AbsolutePixelValue(green-q->green) < MagickEpsilon) &&
8270       (AbsolutePixelValue(blue-q->blue) < MagickEpsilon))
8271     return(MagickTrue);
8272   return(MagickFalse);
8273 }
8274 
8275 #if defined(PNG_tIME_SUPPORTED)
write_tIME_chunk(Image * image,png_struct * ping,png_info * info,const char * timestamp,ExceptionInfo * exception)8276 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8277   const char *timestamp,ExceptionInfo *exception)
8278 {
8279   int
8280     ret;
8281 
8282   int
8283     day,
8284     hour,
8285     minute,
8286     month,
8287     second,
8288     year;
8289 
8290   int
8291     addhours=0,
8292     addminutes=0;
8293 
8294   png_time
8295     ptime;
8296 
8297   assert(timestamp != (const char *) NULL);
8298   LogMagickEvent(CoderEvent,GetMagickModule(),
8299       "  Writing tIME chunk: timestamp property is %30s\n",timestamp);
8300   ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d",&year,&month,&day,&hour,
8301       &minute, &second);
8302   addhours=0;
8303   addminutes=0;
8304   ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d%d:%d",&year,&month,&day,&hour,
8305       &minute, &second, &addhours, &addminutes);
8306     LogMagickEvent(CoderEvent,GetMagickModule(),
8307       "   Date format specified for png:tIME=%s" ,timestamp);
8308     LogMagickEvent(CoderEvent,GetMagickModule(),
8309       "      ret=%d,y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, as=%d",
8310       ret,year,month,day,hour,minute,second,addhours,addminutes);
8311   if (ret < 6)
8312   {
8313     LogMagickEvent(CoderEvent,GetMagickModule(),
8314       "      Invalid date, ret=%d",ret);
8315     (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8316       "Invalid date format specified for png:tIME","`%s' (ret=%d)",
8317       image->filename,ret);
8318     return;
8319   }
8320   if (addhours < 0)
8321   {
8322     addhours+=24;
8323     addminutes=-addminutes;
8324     day--;
8325   }
8326   hour+=addhours;
8327   minute+=addminutes;
8328   if (day == 0)
8329   {
8330     month--;
8331     day=31;
8332     if(month == 2)
8333       day=28;
8334     else
8335     {
8336       if(month == 4 || month == 6 || month == 9 || month == 11)
8337         day=30;
8338       else
8339         day=31;
8340     }
8341   }
8342   if (month == 0)
8343   {
8344     month++;
8345     year--;
8346   }
8347   if (minute > 59)
8348   {
8349      hour++;
8350      minute-=60;
8351   }
8352   if (hour > 23)
8353   {
8354      day ++;
8355      hour -=24;
8356   }
8357   if (hour < 0)
8358   {
8359      day --;
8360      hour +=24;
8361   }
8362   /* To do: fix this for leap years */
8363   if (day > 31 || (month == 2 && day > 28) || ((month == 4 || month == 6 ||
8364       month == 9 || month == 11) && day > 30))
8365   {
8366      month++;
8367      day = 1;
8368   }
8369   if (month > 12)
8370   {
8371      year++;
8372      month=1;
8373   }
8374 
8375   ptime.year = year;
8376   ptime.month = month;
8377   ptime.day = day;
8378   ptime.hour = hour;
8379   ptime.minute = minute;
8380   ptime.second = second;
8381   png_convert_from_time_t(&ptime,GetMagickTime());
8382   LogMagickEvent(CoderEvent,GetMagickModule(),
8383       "      png_set_tIME: y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, am=%d",
8384       ptime.year, ptime.month, ptime.day, ptime.hour, ptime.minute,
8385       ptime.second, addhours, addminutes);
8386   png_set_tIME(ping,info,&ptime);
8387 }
8388 #endif
8389 
8390 /* Write one PNG image */
WriteOnePNGImage(MngInfo * mng_info,const ImageInfo * IMimage_info,Image * IMimage,ExceptionInfo * exception)8391 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8392   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8393 {
8394   char
8395     im_vers[32],
8396     libpng_runv[32],
8397     libpng_vers[32],
8398     zlib_runv[32],
8399     zlib_vers[32];
8400 
8401   Image
8402     *image;
8403 
8404   ImageInfo
8405     *image_info;
8406 
8407   char
8408     *name,
8409     s[2];
8410 
8411   const char
8412     *property,
8413     *value;
8414 
8415   const StringInfo
8416     *profile;
8417 
8418   int
8419     num_passes,
8420     pass,
8421     ping_wrote_caNv;
8422 
8423   png_byte
8424      ping_trans_alpha[256];
8425 
8426   png_color
8427      palette[257];
8428 
8429   png_color_16
8430     ping_background,
8431     ping_trans_color;
8432 
8433   png_info
8434     *ping_info;
8435 
8436   png_struct
8437     *ping;
8438 
8439   png_uint_32
8440     ping_height,
8441     ping_width;
8442 
8443   ssize_t
8444     y;
8445 
8446   MagickBooleanType
8447     image_matte,
8448     logging,
8449     matte,
8450 
8451     ping_have_blob,
8452     ping_have_cheap_transparency,
8453     ping_have_color,
8454     ping_have_non_bw,
8455     ping_have_PLTE,
8456     ping_have_bKGD,
8457     ping_have_eXIf,
8458     ping_have_iCCP,
8459     ping_have_pHYs,
8460     ping_have_sRGB,
8461     ping_have_tRNS,
8462 
8463     ping_exclude_bKGD,
8464     ping_exclude_cHRM,
8465     ping_exclude_date,
8466     /* ping_exclude_EXIF, */
8467     ping_exclude_eXIf,
8468     ping_exclude_gAMA,
8469     ping_exclude_iCCP,
8470     /* ping_exclude_iTXt, */
8471     ping_exclude_oFFs,
8472     ping_exclude_pHYs,
8473     ping_exclude_sRGB,
8474     ping_exclude_tEXt,
8475     ping_exclude_tIME,
8476     /* ping_exclude_tRNS, */
8477     ping_exclude_caNv,
8478     ping_exclude_zCCP, /* hex-encoded iCCP */
8479     ping_exclude_zTXt,
8480 
8481     ping_preserve_colormap,
8482     ping_preserve_iCCP,
8483     ping_need_colortype_warning,
8484 
8485     status,
8486     tried_332,
8487     tried_333,
8488     tried_444;
8489 
8490   MemoryInfo
8491     *volatile pixel_info;
8492 
8493   QuantumInfo
8494     *quantum_info;
8495 
8496   PNGErrorInfo
8497     error_info;
8498 
8499   ssize_t
8500     i,
8501     x;
8502 
8503   unsigned char
8504     *ping_pixels;
8505 
8506   volatile int
8507     image_colors,
8508     ping_bit_depth,
8509     ping_color_type,
8510     ping_interlace_method,
8511     ping_compression_method,
8512     ping_filter_method,
8513     ping_num_trans;
8514 
8515   volatile size_t
8516     image_depth,
8517     old_bit_depth;
8518 
8519   size_t
8520     quality,
8521     rowbytes,
8522     save_image_depth;
8523 
8524   int
8525     j,
8526     number_colors,
8527     number_opaque,
8528     number_semitransparent,
8529     number_transparent,
8530     ping_pHYs_unit_type;
8531 
8532   png_uint_32
8533     ping_pHYs_x_resolution,
8534     ping_pHYs_y_resolution;
8535 
8536   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8537     "  Enter WriteOnePNGImage()");
8538 
8539   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8540   if (image == (Image *) NULL)
8541     return(MagickFalse);
8542   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8543 
8544   /* Define these outside of the following "if logging()" block so they will
8545    * show in debuggers.
8546    */
8547   *im_vers='\0';
8548   (void) ConcatenateMagickString(im_vers,
8549          MagickLibVersionText,MagickPathExtent);
8550   (void) ConcatenateMagickString(im_vers,
8551          MagickLibAddendum,MagickPathExtent);
8552 
8553   *libpng_vers='\0';
8554   (void) ConcatenateMagickString(libpng_vers,
8555          PNG_LIBPNG_VER_STRING,32);
8556   *libpng_runv='\0';
8557   (void) ConcatenateMagickString(libpng_runv,
8558          png_get_libpng_ver(NULL),32);
8559 
8560   *zlib_vers='\0';
8561   (void) ConcatenateMagickString(zlib_vers,
8562          ZLIB_VERSION,32);
8563   *zlib_runv='\0';
8564   (void) ConcatenateMagickString(zlib_runv,
8565          zlib_version,32);
8566 
8567   if (logging != MagickFalse)
8568     {
8569        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8570           "    IM version     = %s", im_vers);
8571        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8572           "    Libpng version = %s", libpng_vers);
8573        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8574        {
8575        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8576           "      running with   %s", libpng_runv);
8577        }
8578        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8579           "    Zlib version   = %s", zlib_vers);
8580        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8581        {
8582        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8583           "      running with   %s", zlib_runv);
8584        }
8585     }
8586 
8587   /* Initialize some stuff */
8588   ping_bit_depth=0,
8589   ping_color_type=0,
8590   ping_interlace_method=0,
8591   ping_compression_method=0,
8592   ping_filter_method=0,
8593   ping_num_trans = 0;
8594 
8595   ping_background.red = 0;
8596   ping_background.green = 0;
8597   ping_background.blue = 0;
8598   ping_background.gray = 0;
8599   ping_background.index = 0;
8600 
8601   ping_trans_color.red=0;
8602   ping_trans_color.green=0;
8603   ping_trans_color.blue=0;
8604   ping_trans_color.gray=0;
8605 
8606   ping_pHYs_unit_type = 0;
8607   ping_pHYs_x_resolution = 0;
8608   ping_pHYs_y_resolution = 0;
8609 
8610   ping_have_blob=MagickFalse;
8611   ping_have_cheap_transparency=MagickFalse;
8612   ping_have_color=MagickTrue;
8613   ping_have_non_bw=MagickTrue;
8614   ping_have_PLTE=MagickFalse;
8615   ping_have_bKGD=MagickFalse;
8616   ping_have_eXIf=MagickTrue;
8617   ping_have_iCCP=MagickFalse;
8618   ping_have_pHYs=MagickFalse;
8619   ping_have_sRGB=MagickFalse;
8620   ping_have_tRNS=MagickFalse;
8621 
8622   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8623   ping_exclude_caNv=mng_info->ping_exclude_caNv;
8624   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8625   ping_exclude_date=mng_info->ping_exclude_date;
8626   ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8627   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8628   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8629   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8630   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8631   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8632   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8633   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8634   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8635   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8636   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8637   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8638 
8639   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8640   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8641   ping_need_colortype_warning = MagickFalse;
8642 
8643   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8644    * i.e., eliminate the ICC profile and set image->rendering_intent.
8645    * Note that this will not involve any changes to the actual pixels
8646    * but merely passes information to applications that read the resulting
8647    * PNG image.
8648    *
8649    * To do: recognize other variants of the sRGB profile, using the CRC to
8650    * verify all recognized variants including the 7 already known.
8651    *
8652    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8653    *
8654    * Use something other than image->rendering_intent to record the fact
8655    * that the sRGB profile was found.
8656    *
8657    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8658    * profile.  Record the Blackpoint Compensation, if any.
8659    */
8660    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8661    {
8662       ResetImageProfileIterator(image);
8663       for (name=GetNextImageProfile(image); name != (char *) NULL; )
8664       {
8665         profile=GetImageProfile(image,name);
8666 
8667         if (profile != (StringInfo *) NULL)
8668           {
8669             if ((LocaleCompare(name,"ICC") == 0) ||
8670                 (LocaleCompare(name,"ICM") == 0))
8671 
8672              {
8673                  int
8674                    icheck,
8675                    got_crc=0;
8676 
8677 
8678                  png_uint_32
8679                    length,
8680                    profile_crc=0;
8681 
8682                  unsigned char
8683                    *data;
8684 
8685                  length=(png_uint_32) GetStringInfoLength(profile);
8686 
8687                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8688                  {
8689                    if (length == sRGB_info[icheck].len)
8690                    {
8691                      if (got_crc == 0)
8692                      {
8693                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8694                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8695                          (unsigned long) length);
8696 
8697                        data=GetStringInfoDatum(profile);
8698                        profile_crc=crc32(0,data,length);
8699 
8700                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8701                            "      with crc=%8x",(unsigned int) profile_crc);
8702                        got_crc++;
8703                      }
8704 
8705                      if (profile_crc == sRGB_info[icheck].crc)
8706                      {
8707                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8708                             "      It is sRGB with rendering intent = %s",
8709                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8710                              sRGB_info[icheck].intent));
8711                         if (image->rendering_intent==UndefinedIntent)
8712                         {
8713                           image->rendering_intent=
8714                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8715                              sRGB_info[icheck].intent);
8716                         }
8717                         ping_exclude_iCCP = MagickTrue;
8718                         ping_exclude_zCCP = MagickTrue;
8719                         ping_have_sRGB = MagickTrue;
8720                         break;
8721                      }
8722                    }
8723                  }
8724                  if (sRGB_info[icheck].len == 0)
8725                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8726                         "    Got %lu-byte ICC profile not recognized as sRGB",
8727                         (unsigned long) length);
8728               }
8729           }
8730         name=GetNextImageProfile(image);
8731       }
8732   }
8733 
8734   number_opaque = 0;
8735   number_semitransparent = 0;
8736   number_transparent = 0;
8737 
8738   if (logging != MagickFalse)
8739     {
8740       if (image->storage_class == UndefinedClass)
8741           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8742           "    image->storage_class=UndefinedClass");
8743       if (image->storage_class == DirectClass)
8744           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8745           "    image->storage_class=DirectClass");
8746       if (image->storage_class == PseudoClass)
8747           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8748           "    image->storage_class=PseudoClass");
8749       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8750           "    image->taint=MagickTrue":
8751           "    image->taint=MagickFalse");
8752       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8753           "    image->gamma=%g", image->gamma);
8754     }
8755 
8756   if (image->storage_class == PseudoClass &&
8757      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8758      mng_info->write_png48 || mng_info->write_png64 ||
8759      (mng_info->write_png_colortype != 1 &&
8760      mng_info->write_png_colortype != 5)))
8761     {
8762       (void) SyncImage(image,exception);
8763       image->storage_class = DirectClass;
8764     }
8765 
8766   if (ping_preserve_colormap == MagickFalse)
8767     {
8768       if ((image->storage_class != PseudoClass) &&
8769           (image->colormap != (PixelInfo *) NULL))
8770         {
8771           /* Free the bogus colormap; it can cause trouble later */
8772            if (logging != MagickFalse)
8773               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8774               "    Freeing bogus colormap");
8775            image->colormap=(PixelInfo *) RelinquishMagickMemory(
8776              image->colormap);
8777         }
8778     }
8779 
8780   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8781     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8782 
8783   /*
8784     Sometimes we get PseudoClass images whose RGB values don't match
8785     the colors in the colormap.  This code syncs the RGB values.
8786   */
8787   image->depth=GetImageQuantumDepth(image,MagickFalse);
8788   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8789      (void) SyncImage(image,exception);
8790 
8791 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8792   if (image->depth > 8)
8793     {
8794       if (logging != MagickFalse)
8795         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8796           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8797 
8798       image->depth=8;
8799     }
8800 #endif
8801 
8802   /* Respect the -depth option */
8803   if (image->depth < 4)
8804     {
8805        Quantum
8806          *r;
8807 
8808        if (image->depth > 2)
8809          {
8810            /* Scale to 4-bit */
8811            LBR04PacketRGBA(image->background_color);
8812 
8813            for (y=0; y < (ssize_t) image->rows; y++)
8814            {
8815              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8816 
8817              if (r == (Quantum *) NULL)
8818                break;
8819 
8820              for (x=0; x < (ssize_t) image->columns; x++)
8821              {
8822                 LBR04PixelRGBA(r);
8823                 r+=GetPixelChannels(image);
8824              }
8825 
8826              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8827                 break;
8828            }
8829 
8830            if (image->storage_class == PseudoClass && image->colormap != NULL)
8831            {
8832              for (i=0; i < (ssize_t) image->colors; i++)
8833              {
8834                LBR04PacketRGBA(image->colormap[i]);
8835              }
8836            }
8837          }
8838        else if (image->depth > 1)
8839          {
8840            /* Scale to 2-bit */
8841            LBR02PacketRGBA(image->background_color);
8842 
8843            for (y=0; y < (ssize_t) image->rows; y++)
8844            {
8845              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8846 
8847              if (r == (Quantum *) NULL)
8848                break;
8849 
8850              for (x=0; x < (ssize_t) image->columns; x++)
8851              {
8852                 LBR02PixelRGBA(r);
8853                 r+=GetPixelChannels(image);
8854              }
8855 
8856              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8857                 break;
8858            }
8859 
8860            if (image->storage_class == PseudoClass && image->colormap != NULL)
8861            {
8862              for (i=0; i < (ssize_t) image->colors; i++)
8863              {
8864                LBR02PacketRGBA(image->colormap[i]);
8865              }
8866            }
8867          }
8868        else
8869          {
8870            /* Scale to 1-bit */
8871            LBR01PacketRGBA(image->background_color);
8872 
8873            for (y=0; y < (ssize_t) image->rows; y++)
8874            {
8875              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8876 
8877              if (r == (Quantum *) NULL)
8878                break;
8879 
8880              for (x=0; x < (ssize_t) image->columns; x++)
8881              {
8882                 LBR01PixelRGBA(r);
8883                 r+=GetPixelChannels(image);
8884              }
8885 
8886              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8887                 break;
8888            }
8889 
8890            if (image->storage_class == PseudoClass && image->colormap != NULL)
8891            {
8892              for (i=0; i < (ssize_t) image->colors; i++)
8893              {
8894                LBR01PacketRGBA(image->colormap[i]);
8895              }
8896            }
8897          }
8898     }
8899 
8900   /* To do: set to next higher multiple of 8 */
8901   if (image->depth < 8)
8902      image->depth=8;
8903 
8904 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8905   /* PNG does not handle depths greater than 16 so reduce it even
8906    * if lossy
8907    */
8908   if (image->depth > 8)
8909       image->depth=16;
8910 #endif
8911 
8912 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8913   if (image->depth > 8)
8914     {
8915       /* To do: fill low byte properly */
8916       image->depth=16;
8917     }
8918 
8919   if (image->depth == 16 && mng_info->write_png_depth != 16)
8920     if (mng_info->write_png8 ||
8921         LosslessReduceDepthOK(image,exception) != MagickFalse)
8922       image->depth = 8;
8923 #endif
8924 
8925   image_colors = (int) image->colors;
8926   number_opaque = (int) image->colors;
8927   number_transparent = 0;
8928   number_semitransparent = 0;
8929 
8930   if (mng_info->write_png_colortype &&
8931      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8932      mng_info->write_png_colortype < 4 &&
8933      image->alpha_trait == UndefinedPixelTrait)))
8934   {
8935      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8936       * are not going to need the result.
8937       */
8938      if (mng_info->write_png_colortype == 1 ||
8939         mng_info->write_png_colortype == 5)
8940        ping_have_color=MagickFalse;
8941 
8942      if (image->alpha_trait != UndefinedPixelTrait)
8943        {
8944          number_transparent = 2;
8945          number_semitransparent = 1;
8946        }
8947   }
8948 
8949   if (mng_info->write_png_colortype < 7)
8950   {
8951   /* BUILD_PALETTE
8952    *
8953    * Normally we run this just once, but in the case of writing PNG8
8954    * we reduce the transparency to binary and run again, then if there
8955    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8956    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8957    * palette.  Then (To do) we take care of a final reduction that is only
8958    * needed if there are still 256 colors present and one of them has both
8959    * transparent and opaque instances.
8960    */
8961 
8962   tried_332 = MagickFalse;
8963   tried_333 = MagickFalse;
8964   tried_444 = MagickFalse;
8965 
8966   if (image->depth != GetImageDepth(image,exception))
8967     (void) SetImageDepth(image,image->depth,exception);
8968   for (j=0; j<6; j++)
8969   {
8970     /*
8971      * Sometimes we get DirectClass images that have 256 colors or fewer.
8972      * This code will build a colormap.
8973      *
8974      * Also, sometimes we get PseudoClass images with an out-of-date
8975      * colormap.  This code will replace the colormap with a new one.
8976      * Sometimes we get PseudoClass images that have more than 256 colors.
8977      * This code will delete the colormap and change the image to
8978      * DirectClass.
8979      *
8980      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8981      * even though it sometimes contains left-over non-opaque values.
8982      *
8983      * Also we gather some information (number of opaque, transparent,
8984      * and semitransparent pixels, and whether the image has any non-gray
8985      * pixels or only black-and-white pixels) that we might need later.
8986      *
8987      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8988      * we need to check for bogus non-opaque values, at least.
8989      */
8990 
8991    int
8992      n;
8993 
8994    PixelInfo
8995      opaque[260],
8996      semitransparent[260],
8997      transparent[260];
8998 
8999    const Quantum
9000      *r;
9001 
9002    Quantum
9003      *q;
9004 
9005    if (logging != MagickFalse)
9006      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9007          "    Enter BUILD_PALETTE:");
9008 
9009    if (logging != MagickFalse)
9010      {
9011        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9012              "      image->columns=%.20g",(double) image->columns);
9013        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9014              "      image->rows=%.20g",(double) image->rows);
9015        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
9017        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9018              "      image->depth=%.20g",(double) image->depth);
9019 
9020        if (image->storage_class == PseudoClass && image->colormap != NULL)
9021        {
9022          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9023              "      Original colormap:");
9024          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9025              "        i    (red,green,blue,alpha)");
9026 
9027          for (i=0; i < (ssize_t) MagickMin(image->colors,256); i++)
9028          {
9029                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9030                    "        %d    (%d,%d,%d,%d)",
9031                     (int) i,
9032                     (int) image->colormap[i].red,
9033                     (int) image->colormap[i].green,
9034                     (int) image->colormap[i].blue,
9035                     (int) image->colormap[i].alpha);
9036          }
9037 
9038          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
9039          {
9040            if (i > 255)
9041              {
9042                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9043                    "        %d    (%d,%d,%d,%d)",
9044                     (int) i,
9045                     (int) image->colormap[i].red,
9046                     (int) image->colormap[i].green,
9047                     (int) image->colormap[i].blue,
9048                     (int) image->colormap[i].alpha);
9049              }
9050          }
9051        }
9052 
9053        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9054            "      image->colors=%d",(int) image->colors);
9055 
9056        if (image->colors == 0)
9057          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9058              "        (zero means unknown)");
9059 
9060        if (ping_preserve_colormap == MagickFalse)
9061          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9062               "      Regenerate the colormap");
9063      }
9064 
9065      image_colors=0;
9066      number_opaque = 0;
9067      number_semitransparent = 0;
9068      number_transparent = 0;
9069 
9070      for (y=0; y < (ssize_t) image->rows; y++)
9071      {
9072        r=GetVirtualPixels(image,0,y,image->columns,1,exception);
9073 
9074        if (r == (const Quantum *) NULL)
9075          break;
9076 
9077        for (x=0; x < (ssize_t) image->columns; x++)
9078        {
9079            if (image->alpha_trait == UndefinedPixelTrait ||
9080               GetPixelAlpha(image,r) == OpaqueAlpha)
9081              {
9082                if (number_opaque < 259)
9083                  {
9084                    if (number_opaque == 0)
9085                      {
9086                        GetPixelInfoPixel(image,r,opaque);
9087                        opaque[0].alpha=OpaqueAlpha;
9088                        number_opaque=1;
9089                      }
9090 
9091                    for (i=0; i< (ssize_t) number_opaque; i++)
9092                      {
9093                        if (IsColorEqual(image,r,opaque+i))
9094                          break;
9095                      }
9096 
9097                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
9098                      {
9099                        number_opaque++;
9100                        GetPixelInfoPixel(image,r,opaque+i);
9101                        opaque[i].alpha=OpaqueAlpha;
9102                      }
9103                  }
9104              }
9105            else if (GetPixelAlpha(image,r) == TransparentAlpha)
9106              {
9107                if (number_transparent < 259)
9108                  {
9109                    if (number_transparent == 0)
9110                      {
9111                        GetPixelInfoPixel(image,r,transparent);
9112                        ping_trans_color.red=(unsigned short)
9113                          GetPixelRed(image,r);
9114                        ping_trans_color.green=(unsigned short)
9115                          GetPixelGreen(image,r);
9116                        ping_trans_color.blue=(unsigned short)
9117                          GetPixelBlue(image,r);
9118                        ping_trans_color.gray=(unsigned short)
9119                          GetPixelGray(image,r);
9120                        number_transparent = 1;
9121                      }
9122 
9123                    for (i=0; i< (ssize_t) number_transparent; i++)
9124                      {
9125                        if (IsColorEqual(image,r,transparent+i))
9126                          break;
9127                      }
9128 
9129                    if (i ==  (ssize_t) number_transparent &&
9130                        number_transparent < 259)
9131                      {
9132                        number_transparent++;
9133                        GetPixelInfoPixel(image,r,transparent+i);
9134                      }
9135                  }
9136              }
9137            else
9138              {
9139                if (number_semitransparent < 259)
9140                  {
9141                    if (number_semitransparent == 0)
9142                      {
9143                        GetPixelInfoPixel(image,r,semitransparent);
9144                        number_semitransparent = 1;
9145                      }
9146 
9147                    for (i=0; i< (ssize_t) number_semitransparent; i++)
9148                      {
9149                        if (IsColorEqual(image,r,semitransparent+i)
9150                            && GetPixelAlpha(image,r) ==
9151                            semitransparent[i].alpha)
9152                          break;
9153                      }
9154 
9155                    if (i ==  (ssize_t) number_semitransparent &&
9156                        number_semitransparent < 259)
9157                      {
9158                        number_semitransparent++;
9159                        GetPixelInfoPixel(image,r,semitransparent+i);
9160                      }
9161                  }
9162              }
9163            r+=GetPixelChannels(image);
9164         }
9165      }
9166 
9167      if (mng_info->write_png8 == MagickFalse &&
9168          ping_exclude_bKGD == MagickFalse)
9169        {
9170          /* Add the background color to the palette, if it
9171           * isn't already there.
9172           */
9173           if (logging != MagickFalse)
9174             {
9175               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9176                   "      Check colormap for background (%d,%d,%d)",
9177                   (int) image->background_color.red,
9178                   (int) image->background_color.green,
9179                   (int) image->background_color.blue);
9180             }
9181           if (number_opaque < 259)
9182             {
9183               for (i=0; i<number_opaque; i++)
9184               {
9185                  if (opaque[i].red == image->background_color.red &&
9186                      opaque[i].green == image->background_color.green &&
9187                      opaque[i].blue == image->background_color.blue)
9188                    break;
9189               }
9190               if (i == number_opaque)
9191                 {
9192                    opaque[i] = image->background_color;
9193                    ping_background.index = i;
9194                    number_opaque++;
9195                    if (logging != MagickFalse)
9196                      {
9197                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9198                            "      background_color index is %d",(int) i);
9199                      }
9200 
9201                 }
9202             }
9203           else if (logging != MagickFalse)
9204               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9205                   "      No room in the colormap to add background color");
9206        }
9207 
9208      image_colors=number_opaque+number_transparent+number_semitransparent;
9209 
9210      if (logging != MagickFalse)
9211        {
9212          if (image_colors > 256)
9213             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9214                   "      image has more than 256 colors");
9215 
9216          else
9217             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9218                   "      image has %d colors",image_colors);
9219        }
9220 
9221      if (ping_preserve_colormap != MagickFalse)
9222        break;
9223 
9224      if (mng_info->write_png_colortype != 7) /* We won't need this info */
9225        {
9226          ping_have_color=MagickFalse;
9227          ping_have_non_bw=MagickFalse;
9228 
9229          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9230          {
9231            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9232               "incompatible colorspace");
9233            ping_have_color=MagickTrue;
9234            ping_have_non_bw=MagickTrue;
9235          }
9236 
9237          if(image_colors > 256)
9238            {
9239              for (y=0; y < (ssize_t) image->rows; y++)
9240              {
9241                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9242 
9243                if (q == (Quantum *) NULL)
9244                  break;
9245 
9246                r=q;
9247                for (x=0; x < (ssize_t) image->columns; x++)
9248                {
9249                  if (GetPixelRed(image,r) != GetPixelGreen(image,r) ||
9250                      GetPixelRed(image,r) != GetPixelBlue(image,r))
9251                    {
9252                       ping_have_color=MagickTrue;
9253                       ping_have_non_bw=MagickTrue;
9254                       break;
9255                    }
9256                  r+=GetPixelChannels(image);
9257                }
9258 
9259                if (ping_have_color != MagickFalse)
9260                  break;
9261 
9262                /* Worst case is black-and-white; we are looking at every
9263                 * pixel twice.
9264                 */
9265 
9266                if (ping_have_non_bw == MagickFalse)
9267                  {
9268                    r=q;
9269                    for (x=0; x < (ssize_t) image->columns; x++)
9270                    {
9271                      if (GetPixelRed(image,r) != 0 &&
9272                          GetPixelRed(image,r) != QuantumRange)
9273                        {
9274                          ping_have_non_bw=MagickTrue;
9275                          break;
9276                        }
9277                      r+=GetPixelChannels(image);
9278                    }
9279                }
9280              }
9281            }
9282        }
9283 
9284      if (image_colors < 257)
9285        {
9286          PixelInfo
9287            colormap[260];
9288 
9289          /*
9290           * Initialize image colormap.
9291           */
9292 
9293          if (logging != MagickFalse)
9294             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9295                   "      Sort the new colormap");
9296 
9297         /* Sort palette, transparent first */;
9298 
9299          n = 0;
9300 
9301          for (i=0; i<number_transparent; i++)
9302             colormap[n++] = transparent[i];
9303 
9304          for (i=0; i<number_semitransparent; i++)
9305             colormap[n++] = semitransparent[i];
9306 
9307          for (i=0; i<number_opaque; i++)
9308             colormap[n++] = opaque[i];
9309 
9310          ping_background.index +=
9311            (number_transparent + number_semitransparent);
9312 
9313          /* image_colors < 257; search the colormap instead of the pixels
9314           * to get ping_have_color and ping_have_non_bw
9315           */
9316          for (i=0; i<n; i++)
9317          {
9318            if (ping_have_color == MagickFalse)
9319              {
9320                 if (colormap[i].red != colormap[i].green ||
9321                     colormap[i].red != colormap[i].blue)
9322                   {
9323                      ping_have_color=MagickTrue;
9324                      ping_have_non_bw=MagickTrue;
9325                      break;
9326                   }
9327               }
9328 
9329            if (ping_have_non_bw == MagickFalse)
9330              {
9331                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9332                    ping_have_non_bw=MagickTrue;
9333              }
9334           }
9335 
9336         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9337             (number_transparent == 0 && number_semitransparent == 0)) &&
9338             (((mng_info->write_png_colortype-1) ==
9339             PNG_COLOR_TYPE_PALETTE) ||
9340             (mng_info->write_png_colortype == 0)))
9341           {
9342             if (logging != MagickFalse)
9343               {
9344                 if (n !=  (ssize_t) image_colors)
9345                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9346                    "   image_colors (%d) and n (%d)  don't match",
9347                    image_colors, n);
9348 
9349                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9350                    "      AcquireImageColormap");
9351               }
9352 
9353             image->colors = image_colors;
9354 
9355             if (AcquireImageColormap(image,image_colors,exception) == MagickFalse)
9356               {
9357                 (void) ThrowMagickException(exception,GetMagickModule(),
9358                   ResourceLimitError,"MemoryAllocationFailed","`%s'",
9359                   image->filename);
9360                 break;
9361               }
9362 
9363             for (i=0; i< (ssize_t) image_colors; i++)
9364                image->colormap[i] = colormap[i];
9365 
9366             if (logging != MagickFalse)
9367               {
9368                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9369                       "      image->colors=%d (%d)",
9370                       (int) image->colors, image_colors);
9371 
9372                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9373                       "      Update the pixel indexes");
9374               }
9375 
9376             /* Sync the pixel indices with the new colormap */
9377 
9378             for (y=0; y < (ssize_t) image->rows; y++)
9379             {
9380               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9381 
9382               if (q == (Quantum *) NULL)
9383                 break;
9384 
9385               for (x=0; x < (ssize_t) image->columns; x++)
9386               {
9387                 for (i=0; i< (ssize_t) image_colors; i++)
9388                 {
9389                   if ((image->alpha_trait == UndefinedPixelTrait ||
9390                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9391                       image->colormap[i].red == GetPixelRed(image,q) &&
9392                       image->colormap[i].green == GetPixelGreen(image,q) &&
9393                       image->colormap[i].blue == GetPixelBlue(image,q))
9394                   {
9395                     SetPixelIndex(image,i,q);
9396                     break;
9397                   }
9398                 }
9399                 q+=GetPixelChannels(image);
9400               }
9401 
9402               if (SyncAuthenticPixels(image,exception) == MagickFalse)
9403                  break;
9404             }
9405           }
9406        }
9407 
9408      if (logging != MagickFalse)
9409        {
9410          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9411             "      image->colors=%d", (int) image->colors);
9412 
9413          if (image->colormap != NULL)
9414            {
9415              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9416                  "       i     (red,green,blue,alpha)");
9417 
9418              for (i=0; i < (ssize_t) image->colors; i++)
9419              {
9420                if (i < 300 || i >= (ssize_t) image->colors - 10)
9421                  {
9422                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9423                        "       %d     (%d,%d,%d,%d)",
9424                         (int) i,
9425                         (int) image->colormap[i].red,
9426                         (int) image->colormap[i].green,
9427                         (int) image->colormap[i].blue,
9428                         (int) image->colormap[i].alpha);
9429                  }
9430              }
9431            }
9432 
9433            if (number_transparent < 257)
9434              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435                    "      number_transparent     = %d",
9436                    number_transparent);
9437            else
9438 
9439              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9440                    "      number_transparent     > 256");
9441 
9442            if (number_opaque < 257)
9443              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9444                    "      number_opaque          = %d",
9445                    number_opaque);
9446 
9447            else
9448              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449                    "      number_opaque          > 256");
9450 
9451            if (number_semitransparent < 257)
9452              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9453                    "      number_semitransparent = %d",
9454                    number_semitransparent);
9455 
9456            else
9457              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9458                    "      number_semitransparent > 256");
9459 
9460            if (ping_have_non_bw == MagickFalse)
9461               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9462                     "      All pixels and the background are black or white");
9463 
9464            else if (ping_have_color == MagickFalse)
9465               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9466                     "      All pixels and the background are gray");
9467 
9468            else
9469               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9470                     "      At least one pixel or the background is non-gray");
9471 
9472            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9473                "    Exit BUILD_PALETTE:");
9474        }
9475 
9476    if (mng_info->write_png8 == MagickFalse)
9477       break;
9478 
9479    /* Make any reductions necessary for the PNG8 format */
9480     if (image_colors <= 256 &&
9481         image_colors != 0 && image->colormap != NULL &&
9482         number_semitransparent == 0 &&
9483         number_transparent <= 1)
9484       break;
9485 
9486     /* PNG8 can't have semitransparent colors so we threshold the
9487      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9488      * transparent color so if more than one is transparent we merge
9489      * them into image->background_color.
9490      */
9491     if (number_semitransparent != 0 || number_transparent > 1)
9492       {
9493         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9494             "    Thresholding the alpha channel to binary");
9495 
9496         for (y=0; y < (ssize_t) image->rows; y++)
9497         {
9498           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9499 
9500           if (q == (Quantum *) NULL)
9501             break;
9502 
9503           for (x=0; x < (ssize_t) image->columns; x++)
9504           {
9505               if (GetPixelAlpha(image,q) < OpaqueAlpha/2)
9506                 {
9507                   SetPixelViaPixelInfo(image,&image->background_color,q);
9508                   SetPixelAlpha(image,TransparentAlpha,q);
9509                 }
9510               else
9511                   SetPixelAlpha(image,OpaqueAlpha,q);
9512               q+=GetPixelChannels(image);
9513           }
9514 
9515           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9516              break;
9517 
9518           if (image_colors != 0 && image_colors <= 256 &&
9519              image->colormap != NULL)
9520             for (i=0; i<image_colors; i++)
9521                 image->colormap[i].alpha =
9522                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9523                     TransparentAlpha : OpaqueAlpha);
9524         }
9525       continue;
9526     }
9527 
9528     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9529      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9530      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9531      * colors or less.
9532      */
9533     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9534       {
9535         if (logging != MagickFalse)
9536            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9537                "    Quantizing the background color to 4-4-4");
9538 
9539         tried_444 = MagickTrue;
9540 
9541         LBR04PacketRGB(image->background_color);
9542 
9543         if (logging != MagickFalse)
9544           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9545               "    Quantizing the pixel colors to 4-4-4");
9546 
9547         if (image->colormap == NULL)
9548         {
9549           for (y=0; y < (ssize_t) image->rows; y++)
9550           {
9551             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9552 
9553             if (q == (Quantum *) NULL)
9554               break;
9555 
9556             for (x=0; x < (ssize_t) image->columns; x++)
9557             {
9558               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9559                   LBR04PixelRGB(q);
9560               q+=GetPixelChannels(image);
9561             }
9562 
9563             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9564                break;
9565           }
9566         }
9567 
9568         else /* Should not reach this; colormap already exists and
9569                 must be <= 256 */
9570         {
9571           if (logging != MagickFalse)
9572               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9573               "    Quantizing the colormap to 4-4-4");
9574 
9575           for (i=0; i<image_colors; i++)
9576           {
9577             LBR04PacketRGB(image->colormap[i]);
9578           }
9579         }
9580         continue;
9581       }
9582 
9583     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9584       {
9585         if (logging != MagickFalse)
9586            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9587                "    Quantizing the background color to 3-3-3");
9588 
9589         tried_333 = MagickTrue;
9590 
9591         LBR03PacketRGB(image->background_color);
9592 
9593         if (logging != MagickFalse)
9594           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9595               "    Quantizing the pixel colors to 3-3-3-1");
9596 
9597         if (image->colormap == NULL)
9598         {
9599           for (y=0; y < (ssize_t) image->rows; y++)
9600           {
9601             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9602 
9603             if (q == (Quantum *) NULL)
9604               break;
9605 
9606             for (x=0; x < (ssize_t) image->columns; x++)
9607             {
9608               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9609                   LBR03RGB(q);
9610               q+=GetPixelChannels(image);
9611             }
9612 
9613             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9614                break;
9615           }
9616         }
9617 
9618         else /* Should not reach this; colormap already exists and
9619                 must be <= 256 */
9620         {
9621           if (logging != MagickFalse)
9622               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9623               "    Quantizing the colormap to 3-3-3-1");
9624           for (i=0; i<image_colors; i++)
9625           {
9626               LBR03PacketRGB(image->colormap[i]);
9627           }
9628         }
9629         continue;
9630       }
9631 
9632     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9633       {
9634         if (logging != MagickFalse)
9635            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9636                "    Quantizing the background color to 3-3-2");
9637 
9638         tried_332 = MagickTrue;
9639 
9640         /* Red and green were already done so we only quantize the blue
9641          * channel
9642          */
9643 
9644         LBR02PacketBlue(image->background_color);
9645 
9646         if (logging != MagickFalse)
9647           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9648               "    Quantizing the pixel colors to 3-3-2-1");
9649 
9650         if (image->colormap == NULL)
9651         {
9652           for (y=0; y < (ssize_t) image->rows; y++)
9653           {
9654             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9655 
9656             if (q == (Quantum *) NULL)
9657               break;
9658 
9659             for (x=0; x < (ssize_t) image->columns; x++)
9660             {
9661               if (GetPixelAlpha(image,q) == OpaqueAlpha)
9662                   LBR02PixelBlue(q);
9663               q+=GetPixelChannels(image);
9664             }
9665 
9666             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9667                break;
9668           }
9669         }
9670 
9671         else /* Should not reach this; colormap already exists and
9672                 must be <= 256 */
9673         {
9674           if (logging != MagickFalse)
9675               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9676               "    Quantizing the colormap to 3-3-2-1");
9677           for (i=0; i<image_colors; i++)
9678           {
9679               LBR02PacketBlue(image->colormap[i]);
9680           }
9681       }
9682       continue;
9683     }
9684 
9685     if (image_colors == 0 || image_colors > 256)
9686     {
9687       /* Take care of special case with 256 opaque colors + 1 transparent
9688        * color.  We don't need to quantize to 2-3-2-1; we only need to
9689        * eliminate one color, so we'll merge the two darkest red
9690        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9691        */
9692       if (logging != MagickFalse)
9693         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9694             "    Merging two dark red background colors to 3-3-2-1");
9695 
9696       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9697           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9698           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9699       {
9700          image->background_color.red=ScaleCharToQuantum(0x24);
9701       }
9702 
9703       if (logging != MagickFalse)
9704         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9705             "    Merging two dark red pixel colors to 3-3-2-1");
9706 
9707       if (image->colormap == NULL)
9708       {
9709         for (y=0; y < (ssize_t) image->rows; y++)
9710         {
9711           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9712 
9713           if (q == (Quantum *) NULL)
9714             break;
9715 
9716           for (x=0; x < (ssize_t) image->columns; x++)
9717           {
9718             if (ScaleQuantumToChar(GetPixelRed(image,q)) == 0x49 &&
9719                 ScaleQuantumToChar(GetPixelGreen(image,q)) == 0x00 &&
9720                 ScaleQuantumToChar(GetPixelBlue(image,q)) == 0x00 &&
9721                 GetPixelAlpha(image,q) == OpaqueAlpha)
9722               {
9723                 SetPixelRed(image,ScaleCharToQuantum(0x24),q);
9724               }
9725             q+=GetPixelChannels(image);
9726           }
9727 
9728           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9729              break;
9730 
9731         }
9732       }
9733 
9734       else
9735       {
9736          for (i=0; i<image_colors; i++)
9737          {
9738             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9739                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9740                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9741             {
9742                image->colormap[i].red=ScaleCharToQuantum(0x24);
9743             }
9744          }
9745       }
9746     }
9747   }
9748   }
9749   /* END OF BUILD_PALETTE */
9750 
9751   /* If we are excluding the tRNS chunk and there is transparency,
9752    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9753    * PNG.
9754    */
9755   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9756      (number_transparent != 0 || number_semitransparent != 0))
9757     {
9758       unsigned int colortype=mng_info->write_png_colortype;
9759 
9760       if (ping_have_color == MagickFalse)
9761         mng_info->write_png_colortype = 5;
9762 
9763       else
9764         mng_info->write_png_colortype = 7;
9765 
9766       if (colortype != 0 &&
9767          mng_info->write_png_colortype != colortype)
9768         ping_need_colortype_warning=MagickTrue;
9769 
9770     }
9771 
9772   /* See if cheap transparency is possible.  It is only possible
9773    * when there is a single transparent color, no semitransparent
9774    * color, and no opaque color that has the same RGB components
9775    * as the transparent color.  We only need this information if
9776    * we are writing a PNG with colortype 0 or 2, and we have not
9777    * excluded the tRNS chunk.
9778    */
9779   if (number_transparent == 1 &&
9780       mng_info->write_png_colortype < 4)
9781     {
9782        ping_have_cheap_transparency = MagickTrue;
9783 
9784        if (number_semitransparent != 0)
9785          ping_have_cheap_transparency = MagickFalse;
9786 
9787        else if (image_colors == 0 || image_colors > 256 ||
9788            image->colormap == NULL)
9789          {
9790            const Quantum
9791              *q;
9792 
9793            for (y=0; y < (ssize_t) image->rows; y++)
9794            {
9795              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9796 
9797              if (q == (Quantum *) NULL)
9798                break;
9799 
9800              for (x=0; x < (ssize_t) image->columns; x++)
9801              {
9802                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9803                      (unsigned short) GetPixelRed(image,q) ==
9804                                      ping_trans_color.red &&
9805                      (unsigned short) GetPixelGreen(image,q) ==
9806                                      ping_trans_color.green &&
9807                      (unsigned short) GetPixelBlue(image,q) ==
9808                                      ping_trans_color.blue)
9809                    {
9810                      ping_have_cheap_transparency = MagickFalse;
9811                      break;
9812                    }
9813 
9814                  q+=GetPixelChannels(image);
9815              }
9816 
9817              if (ping_have_cheap_transparency == MagickFalse)
9818                 break;
9819            }
9820          }
9821        else
9822          {
9823             /* Assuming that image->colormap[0] is the one transparent color
9824              * and that all others are opaque.
9825              */
9826             if (image_colors > 1)
9827               for (i=1; i<image_colors; i++)
9828                 if (image->colormap[i].red == image->colormap[0].red &&
9829                     image->colormap[i].green == image->colormap[0].green &&
9830                     image->colormap[i].blue == image->colormap[0].blue)
9831                   {
9832                      ping_have_cheap_transparency = MagickFalse;
9833                      break;
9834                   }
9835          }
9836 
9837        if (logging != MagickFalse)
9838          {
9839            if (ping_have_cheap_transparency == MagickFalse)
9840              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841                  "   Cheap transparency is not possible.");
9842 
9843            else
9844              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845                  "   Cheap transparency is possible.");
9846          }
9847      }
9848   else
9849     ping_have_cheap_transparency = MagickFalse;
9850 
9851   image_depth=image->depth;
9852 
9853   quantum_info = (QuantumInfo *) NULL;
9854   number_colors=0;
9855   image_colors=(int) image->colors;
9856   image_matte=image->alpha_trait !=
9857         UndefinedPixelTrait ? MagickTrue : MagickFalse;
9858 
9859   if (mng_info->write_png_colortype < 5)
9860     mng_info->IsPalette=image->storage_class == PseudoClass &&
9861       image_colors <= 256 && image->colormap != NULL;
9862   else
9863     mng_info->IsPalette = MagickFalse;
9864 
9865   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9866      (image->colors == 0 || image->colormap == NULL))
9867     {
9868       image_info=DestroyImageInfo(image_info);
9869       image=DestroyImage(image);
9870       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9871           "Cannot write PNG8 or color-type 3; colormap is NULL",
9872           "`%s'",IMimage->filename);
9873       return(MagickFalse);
9874     }
9875 
9876   /*
9877     Allocate the PNG structures
9878   */
9879 #ifdef PNG_USER_MEM_SUPPORTED
9880  error_info.image=image;
9881  error_info.exception=exception;
9882   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9883     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9884     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9885 
9886 #else
9887   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9888     MagickPNGErrorHandler,MagickPNGWarningHandler);
9889 
9890 #endif
9891   if (ping == (png_struct *) NULL)
9892     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9893 
9894   ping_info=png_create_info_struct(ping);
9895 
9896   if (ping_info == (png_info *) NULL)
9897     {
9898       png_destroy_write_struct(&ping,(png_info **) NULL);
9899       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9900     }
9901 
9902   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9903   pixel_info=(MemoryInfo *) NULL;
9904 
9905   if (setjmp(png_jmpbuf(ping)))
9906     {
9907       /*
9908         PNG write failed.
9909       */
9910 #ifdef PNG_DEBUG
9911      if (image_info->verbose)
9912         (void) printf("PNG write has failed.\n");
9913 #endif
9914       png_destroy_write_struct(&ping,&ping_info);
9915 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9916       UnlockSemaphoreInfo(ping_semaphore);
9917 #endif
9918 
9919       if (pixel_info != (MemoryInfo *) NULL)
9920         pixel_info=RelinquishVirtualMemory(pixel_info);
9921 
9922       if (quantum_info != (QuantumInfo *) NULL)
9923         quantum_info=DestroyQuantumInfo(quantum_info);
9924 
9925       if (ping_have_blob != MagickFalse)
9926           (void) CloseBlob(image);
9927       image_info=DestroyImageInfo(image_info);
9928       image=DestroyImage(image);
9929       return(MagickFalse);
9930     }
9931 
9932   /* {  For navigation to end of SETJMP-protected block.  Within this
9933    *    block, use png_error() instead of Throwing an Exception, to ensure
9934    *    that libpng is able to clean up, and that the semaphore is unlocked.
9935    */
9936 
9937 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9938   LockSemaphoreInfo(ping_semaphore);
9939 #endif
9940 
9941 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9942   /* Allow benign errors */
9943   png_set_benign_errors(ping, 1);
9944 #endif
9945 
9946 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9947   /* Reject images with too many rows or columns */
9948   png_set_user_limits(ping,
9949     (png_uint_32) MagickMin(0x7fffffffL,
9950         GetMagickResourceLimit(WidthResource)),
9951     (png_uint_32) MagickMin(0x7fffffffL,
9952         GetMagickResourceLimit(HeightResource)));
9953 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9954 
9955   /*
9956     Prepare PNG for writing.
9957   */
9958 
9959 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9960   if (mng_info->write_mng)
9961   {
9962      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9963 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9964      /* Disable new libpng-1.5.10 feature when writing a MNG because
9965       * zero-length PLTE is OK
9966       */
9967      png_set_check_for_invalid_index (ping, 0);
9968 # endif
9969   }
9970 
9971 #else
9972 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9973   if (mng_info->write_mng)
9974      png_permit_empty_plte(ping,MagickTrue);
9975 
9976 # endif
9977 #endif
9978 
9979   x=0;
9980 
9981   ping_width=(png_uint_32) image->columns;
9982   ping_height=(png_uint_32) image->rows;
9983 
9984   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9985      image_depth=8;
9986 
9987   if (mng_info->write_png48 || mng_info->write_png64)
9988      image_depth=16;
9989 
9990   if (mng_info->write_png_depth != 0)
9991      image_depth=mng_info->write_png_depth;
9992 
9993   /* Adjust requested depth to next higher valid depth if necessary */
9994   if (image_depth > 8)
9995      image_depth=16;
9996 
9997   if ((image_depth > 4) && (image_depth < 8))
9998      image_depth=8;
9999 
10000   if (image_depth == 3)
10001      image_depth=4;
10002 
10003   if (logging != MagickFalse)
10004     {
10005      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10006         "    width=%.20g",(double) ping_width);
10007      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10008         "    height=%.20g",(double) ping_height);
10009      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10010         "    image_matte=%.20g",(double) image->alpha_trait);
10011      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10012         "    image->depth=%.20g",(double) image->depth);
10013      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10014         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
10015     }
10016 
10017   save_image_depth=image_depth;
10018   ping_bit_depth=(png_byte) save_image_depth;
10019 
10020 
10021 #if defined(PNG_pHYs_SUPPORTED)
10022   if (ping_exclude_pHYs == MagickFalse)
10023   {
10024   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
10025       (!mng_info->write_mng || !mng_info->equal_physs))
10026     {
10027       if (logging != MagickFalse)
10028         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10029             "    Setting up pHYs chunk");
10030 
10031       if (image->units == PixelsPerInchResolution)
10032         {
10033           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
10034           ping_pHYs_x_resolution=
10035              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
10036           ping_pHYs_y_resolution=
10037              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
10038         }
10039 
10040       else if (image->units == PixelsPerCentimeterResolution)
10041         {
10042           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
10043           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
10044           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
10045         }
10046 
10047       else
10048         {
10049           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
10050           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
10051           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
10052         }
10053 
10054       if (logging != MagickFalse)
10055         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10056           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
10057           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
10058           (int) ping_pHYs_unit_type);
10059        ping_have_pHYs = MagickTrue;
10060     }
10061   }
10062 #endif
10063 
10064   if (ping_exclude_bKGD == MagickFalse)
10065   {
10066   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
10067     {
10068        unsigned int
10069          mask;
10070 
10071        mask=0xffff;
10072        if (ping_bit_depth == 8)
10073           mask=0x00ff;
10074 
10075        if (ping_bit_depth == 4)
10076           mask=0x000f;
10077 
10078        if (ping_bit_depth == 2)
10079           mask=0x0003;
10080 
10081        if (ping_bit_depth == 1)
10082           mask=0x0001;
10083 
10084        ping_background.red=(png_uint_16)
10085          (ScaleQuantumToShort(image->background_color.red) & mask);
10086 
10087        ping_background.green=(png_uint_16)
10088          (ScaleQuantumToShort(image->background_color.green) & mask);
10089 
10090        ping_background.blue=(png_uint_16)
10091          (ScaleQuantumToShort(image->background_color.blue) & mask);
10092 
10093        ping_background.gray=(png_uint_16) ping_background.green;
10094     }
10095 
10096   if (logging != MagickFalse)
10097     {
10098       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10099           "    Setting up bKGD chunk (1)");
10100       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10101           "      background_color index is %d",
10102           (int) ping_background.index);
10103 
10104       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105           "    ping_bit_depth=%d",ping_bit_depth);
10106     }
10107 
10108   ping_have_bKGD = MagickTrue;
10109   }
10110 
10111   /*
10112     Select the color type.
10113   */
10114   matte=image_matte;
10115   old_bit_depth=0;
10116 
10117   if (mng_info->IsPalette && mng_info->write_png8)
10118     {
10119       /* To do: make this a function cause it's used twice, except
10120          for reducing the sample depth from 8. */
10121 
10122       number_colors=image_colors;
10123 
10124       ping_have_tRNS=MagickFalse;
10125 
10126       /*
10127         Set image palette.
10128       */
10129       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10130 
10131       if (logging != MagickFalse)
10132         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10133             "  Setting up PLTE chunk with %d colors (%d)",
10134             number_colors, image_colors);
10135 
10136       for (i=0; i < (ssize_t) number_colors; i++)
10137       {
10138         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10139         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10140         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10141         if (logging != MagickFalse)
10142           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10143 #if MAGICKCORE_QUANTUM_DEPTH == 8
10144             "    %3ld (%3d,%3d,%3d)",
10145 #else
10146             "    %5ld (%5d,%5d,%5d)",
10147 #endif
10148             (long) i,palette[i].red,palette[i].green,palette[i].blue);
10149 
10150       }
10151 
10152       ping_have_PLTE=MagickTrue;
10153       image_depth=ping_bit_depth;
10154       ping_num_trans=0;
10155 
10156       if (matte != MagickFalse)
10157       {
10158           /*
10159             Identify which colormap entry is transparent.
10160           */
10161           assert(number_colors <= 256);
10162           assert(image->colormap != NULL);
10163 
10164           for (i=0; i < (ssize_t) number_transparent; i++)
10165              ping_trans_alpha[i]=0;
10166 
10167 
10168           ping_num_trans=(unsigned short) (number_transparent +
10169              number_semitransparent);
10170 
10171           if (ping_num_trans == 0)
10172              ping_have_tRNS=MagickFalse;
10173 
10174           else
10175              ping_have_tRNS=MagickTrue;
10176       }
10177 
10178       if (ping_exclude_bKGD == MagickFalse)
10179       {
10180        /*
10181         * Identify which colormap entry is the background color.
10182         */
10183 
10184         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10185           if (IsPNGColorEqual(ping_background,image->colormap[i]))
10186             break;
10187 
10188         ping_background.index=(png_byte) i;
10189 
10190         if (logging != MagickFalse)
10191           {
10192             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10193                  "      background_color index is %d",
10194                  (int) ping_background.index);
10195           }
10196       }
10197     } /* end of write_png8 */
10198 
10199   else if (mng_info->write_png_colortype == 1)
10200     {
10201       image_matte=MagickFalse;
10202       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10203     }
10204 
10205   else if (mng_info->write_png24 || mng_info->write_png48 ||
10206       mng_info->write_png_colortype == 3)
10207     {
10208       image_matte=MagickFalse;
10209       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10210     }
10211 
10212   else if (mng_info->write_png32 || mng_info->write_png64 ||
10213       mng_info->write_png_colortype == 7)
10214     {
10215       image_matte=MagickTrue;
10216       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10217     }
10218 
10219   else /* mng_info->write_pngNN not specified */
10220     {
10221       image_depth=ping_bit_depth;
10222 
10223       if (mng_info->write_png_colortype != 0)
10224         {
10225           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10226 
10227           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10228               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10229             image_matte=MagickTrue;
10230 
10231           else
10232             image_matte=MagickFalse;
10233 
10234           if (logging != MagickFalse)
10235              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10236              "   PNG colortype %d was specified:",(int) ping_color_type);
10237         }
10238 
10239       else /* write_png_colortype not specified */
10240         {
10241           if (logging != MagickFalse)
10242              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10243              "  Selecting PNG colortype:");
10244 
10245           if (image_info->type == TrueColorType)
10246             {
10247               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10248               image_matte=MagickFalse;
10249             }
10250           else if (image_info->type == TrueColorAlphaType)
10251             {
10252               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10253               image_matte=MagickTrue;
10254             }
10255           else if (image_info->type == PaletteType ||
10256                    image_info->type == PaletteAlphaType)
10257             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10258           else
10259             {
10260               if (ping_have_color == MagickFalse)
10261                 {
10262                   if (image_matte == MagickFalse)
10263                     {
10264                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10265                       image_matte=MagickFalse;
10266                     }
10267 
10268                   else
10269                     {
10270                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10271                       image_matte=MagickTrue;
10272                     }
10273                 }
10274               else
10275                 {
10276                   if (image_matte == MagickFalse)
10277                     {
10278                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10279                       image_matte=MagickFalse;
10280                     }
10281 
10282                   else
10283                     {
10284                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10285                       image_matte=MagickTrue;
10286                     }
10287                  }
10288             }
10289 
10290         }
10291 
10292       if (logging != MagickFalse)
10293          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10294          "    Selected PNG colortype=%d",ping_color_type);
10295 
10296       if (ping_bit_depth < 8)
10297         {
10298           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10299               ping_color_type == PNG_COLOR_TYPE_RGB ||
10300               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10301             ping_bit_depth=8;
10302         }
10303 
10304       old_bit_depth=ping_bit_depth;
10305 
10306       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10307         {
10308           if (image->alpha_trait == UndefinedPixelTrait &&
10309                ping_have_non_bw == MagickFalse)
10310              ping_bit_depth=1;
10311         }
10312 
10313       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10314         {
10315            size_t one = 1;
10316            ping_bit_depth=1;
10317 
10318            if (image->colors == 0)
10319            {
10320               /* DO SOMETHING */
10321                 png_error(ping,"image has 0 colors");
10322            }
10323 
10324            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10325              ping_bit_depth <<= 1;
10326         }
10327 
10328       if (logging != MagickFalse)
10329          {
10330            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10331             "    Number of colors: %.20g",(double) image_colors);
10332 
10333            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10334             "    Tentative PNG bit depth: %d",ping_bit_depth);
10335          }
10336 
10337       if (ping_bit_depth < (int) mng_info->write_png_depth)
10338          ping_bit_depth = mng_info->write_png_depth;
10339     }
10340   (void) old_bit_depth;
10341   image_depth=ping_bit_depth;
10342 
10343   if (logging != MagickFalse)
10344     {
10345       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10346         "    Tentative PNG color type: %s (%.20g)",
10347         PngColorTypeToString(ping_color_type),
10348         (double) ping_color_type);
10349 
10350       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10351         "    image_info->type: %.20g",(double) image_info->type);
10352 
10353       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10354         "    image_depth: %.20g",(double) image_depth);
10355 
10356       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10357 
10358         "    image->depth: %.20g",(double) image->depth);
10359 
10360       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10361         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
10362     }
10363 
10364   if (matte != MagickFalse)
10365     {
10366       if (mng_info->IsPalette)
10367         {
10368           if (mng_info->write_png_colortype == 0)
10369             {
10370               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10371 
10372               if (ping_have_color != MagickFalse)
10373                  ping_color_type=PNG_COLOR_TYPE_RGBA;
10374             }
10375 
10376           /*
10377            * Determine if there is any transparent color.
10378           */
10379           if (number_transparent + number_semitransparent == 0)
10380             {
10381               /*
10382                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
10383               */
10384 
10385               image_matte=MagickFalse;
10386 
10387               if (mng_info->write_png_colortype == 0)
10388                 ping_color_type&=0x03;
10389             }
10390 
10391           else
10392             {
10393               unsigned int
10394                 mask;
10395 
10396               mask=0xffff;
10397 
10398               if (ping_bit_depth == 8)
10399                  mask=0x00ff;
10400 
10401               if (ping_bit_depth == 4)
10402                  mask=0x000f;
10403 
10404               if (ping_bit_depth == 2)
10405                  mask=0x0003;
10406 
10407               if (ping_bit_depth == 1)
10408                  mask=0x0001;
10409 
10410               ping_trans_color.red=(png_uint_16)
10411                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10412 
10413               ping_trans_color.green=(png_uint_16)
10414                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10415 
10416               ping_trans_color.blue=(png_uint_16)
10417                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10418 
10419               ping_trans_color.gray=(png_uint_16)
10420                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10421                    image->colormap)) & mask);
10422 
10423               ping_trans_color.index=(png_byte) 0;
10424 
10425               ping_have_tRNS=MagickTrue;
10426             }
10427 
10428           if (ping_have_tRNS != MagickFalse)
10429             {
10430               /*
10431                * Determine if there is one and only one transparent color
10432                * and if so if it is fully transparent.
10433                */
10434               if (ping_have_cheap_transparency == MagickFalse)
10435                 ping_have_tRNS=MagickFalse;
10436             }
10437 
10438           if (ping_have_tRNS != MagickFalse)
10439             {
10440               if (mng_info->write_png_colortype == 0)
10441                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10442 
10443               if (image_depth == 8)
10444                 {
10445                   ping_trans_color.red&=0xff;
10446                   ping_trans_color.green&=0xff;
10447                   ping_trans_color.blue&=0xff;
10448                   ping_trans_color.gray&=0xff;
10449                 }
10450             }
10451         }
10452       else
10453         {
10454           if (image_depth == 8)
10455             {
10456               ping_trans_color.red&=0xff;
10457               ping_trans_color.green&=0xff;
10458               ping_trans_color.blue&=0xff;
10459               ping_trans_color.gray&=0xff;
10460             }
10461         }
10462     }
10463 
10464     matte=image_matte;
10465 
10466     if (ping_have_tRNS != MagickFalse)
10467       image_matte=MagickFalse;
10468 
10469     if ((mng_info->IsPalette) &&
10470         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10471         ping_have_color == MagickFalse &&
10472         (image_matte == MagickFalse || image_depth >= 8))
10473       {
10474         size_t one=1;
10475 
10476         if (image_matte != MagickFalse)
10477           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10478 
10479         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10480           {
10481             ping_color_type=PNG_COLOR_TYPE_GRAY;
10482 
10483             if (save_image_depth == 16 && image_depth == 8)
10484               {
10485                 if (logging != MagickFalse)
10486                   {
10487                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10488                         "  Scaling ping_trans_color (0)");
10489                   }
10490                     ping_trans_color.gray*=0x0101;
10491               }
10492           }
10493 
10494         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10495           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10496 
10497         if ((image_colors == 0) ||
10498              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10499           image_colors=(int) (one << image_depth);
10500 
10501         if (image_depth > 8)
10502           ping_bit_depth=16;
10503 
10504         else
10505           {
10506             ping_bit_depth=8;
10507             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10508               {
10509                 if(!mng_info->write_png_depth)
10510                   {
10511                     ping_bit_depth=1;
10512 
10513                     while ((int) (one << ping_bit_depth)
10514                         < (ssize_t) image_colors)
10515                       ping_bit_depth <<= 1;
10516                   }
10517               }
10518 
10519             else if (ping_color_type ==
10520                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10521                 mng_info->IsPalette)
10522               {
10523               /* Check if grayscale is reducible */
10524 
10525                 int
10526                   depth_4_ok=MagickTrue,
10527                   depth_2_ok=MagickTrue,
10528                   depth_1_ok=MagickTrue;
10529 
10530                 for (i=0; i < (ssize_t) image_colors; i++)
10531                 {
10532                    unsigned char
10533                      intensity;
10534 
10535                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10536 
10537                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10538                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10539                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10540                      depth_2_ok=depth_1_ok=MagickFalse;
10541                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10542                      depth_1_ok=MagickFalse;
10543                 }
10544 
10545                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10546                   ping_bit_depth=1;
10547 
10548                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10549                   ping_bit_depth=2;
10550 
10551                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10552                   ping_bit_depth=4;
10553               }
10554           }
10555 
10556           image_depth=ping_bit_depth;
10557       }
10558 
10559     else
10560 
10561       if (mng_info->IsPalette)
10562       {
10563         number_colors=image_colors;
10564 
10565         if (image_depth <= 8)
10566           {
10567             /*
10568               Set image palette.
10569             */
10570             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10571 
10572             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10573               {
10574                 for (i=0; i < (ssize_t) number_colors; i++)
10575                 {
10576                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10577                   palette[i].green=
10578                     ScaleQuantumToChar(image->colormap[i].green);
10579                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10580                 }
10581 
10582                 if (logging != MagickFalse)
10583                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10584                     "  Setting up PLTE chunk with %d colors",
10585                     number_colors);
10586 
10587                 ping_have_PLTE=MagickTrue;
10588               }
10589 
10590             /* color_type is PNG_COLOR_TYPE_PALETTE */
10591             if (mng_info->write_png_depth == 0)
10592               {
10593                 size_t
10594                   one;
10595 
10596                 ping_bit_depth=1;
10597                 one=1;
10598 
10599                 while ((one << ping_bit_depth) < (size_t) number_colors)
10600                   ping_bit_depth <<= 1;
10601               }
10602 
10603             ping_num_trans=0;
10604 
10605             if (matte != MagickFalse)
10606               {
10607                 /*
10608                  * Set up trans_colors array.
10609                  */
10610                 assert(number_colors <= 256);
10611 
10612                 ping_num_trans=(unsigned short) (number_transparent +
10613                   number_semitransparent);
10614 
10615                 if (ping_num_trans == 0)
10616                   ping_have_tRNS=MagickFalse;
10617 
10618                 else
10619                   {
10620                     if (logging != MagickFalse)
10621                       {
10622                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10623                           "  Scaling ping_trans_color (1)");
10624                       }
10625                     ping_have_tRNS=MagickTrue;
10626 
10627                     for (i=0; i < ping_num_trans; i++)
10628                     {
10629                        ping_trans_alpha[i]= (png_byte)
10630                          ScaleQuantumToChar(image->colormap[i].alpha);
10631                     }
10632                   }
10633               }
10634           }
10635       }
10636 
10637     else
10638       {
10639 
10640         if (image_depth < 8)
10641           image_depth=8;
10642 
10643         if ((save_image_depth == 16) && (image_depth == 8))
10644           {
10645             if (logging != MagickFalse)
10646               {
10647                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10648                   "    Scaling ping_trans_color from (%d,%d,%d)",
10649                   (int) ping_trans_color.red,
10650                   (int) ping_trans_color.green,
10651                   (int) ping_trans_color.blue);
10652               }
10653 
10654             ping_trans_color.red*=0x0101;
10655             ping_trans_color.green*=0x0101;
10656             ping_trans_color.blue*=0x0101;
10657             ping_trans_color.gray*=0x0101;
10658 
10659             if (logging != MagickFalse)
10660               {
10661                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10662                   "    to (%d,%d,%d)",
10663                   (int) ping_trans_color.red,
10664                   (int) ping_trans_color.green,
10665                   (int) ping_trans_color.blue);
10666               }
10667           }
10668       }
10669 
10670     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10671          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10672 
10673     /*
10674       Adjust background and transparency samples in sub-8-bit grayscale files.
10675     */
10676     if (ping_bit_depth < 8 && ping_color_type ==
10677         PNG_COLOR_TYPE_GRAY)
10678       {
10679          png_uint_16
10680            maxval;
10681 
10682          size_t
10683            one=1;
10684 
10685          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10686 
10687          if (ping_exclude_bKGD == MagickFalse)
10688          {
10689 
10690          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10691            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10692            &image->background_color))) +.5)));
10693 
10694          if (logging != MagickFalse)
10695            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10696              "  Setting up bKGD chunk (2)");
10697          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10698              "      background_color index is %d",
10699              (int) ping_background.index);
10700 
10701          ping_have_bKGD = MagickTrue;
10702          }
10703 
10704          if (logging != MagickFalse)
10705            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10706              "  Scaling ping_trans_color.gray from %d",
10707              (int)ping_trans_color.gray);
10708 
10709          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10710            ping_trans_color.gray)+.5);
10711 
10712          if (logging != MagickFalse)
10713            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10714              "      to %d", (int)ping_trans_color.gray);
10715       }
10716 
10717   if (ping_exclude_bKGD == MagickFalse)
10718   {
10719     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10720       {
10721         /*
10722            Identify which colormap entry is the background color.
10723         */
10724 
10725         number_colors=image_colors;
10726 
10727         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10728           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10729             break;
10730 
10731         ping_background.index=(png_byte) i;
10732 
10733         if (logging != MagickFalse)
10734           {
10735             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10736               "  Setting up bKGD chunk with index=%d",(int) i);
10737           }
10738 
10739         if (i < (ssize_t) number_colors)
10740           {
10741             ping_have_bKGD = MagickTrue;
10742 
10743             if (logging != MagickFalse)
10744               {
10745                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10746                   "     background   =(%d,%d,%d)",
10747                         (int) ping_background.red,
10748                         (int) ping_background.green,
10749                         (int) ping_background.blue);
10750               }
10751           }
10752 
10753         else  /* Can't happen */
10754           {
10755             if (logging != MagickFalse)
10756               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10757                   "      No room in PLTE to add bKGD color");
10758             ping_have_bKGD = MagickFalse;
10759           }
10760       }
10761   }
10762 
10763   if (logging != MagickFalse)
10764     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10765       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10766       ping_color_type);
10767   /*
10768     Initialize compression level and filtering.
10769   */
10770   if (logging != MagickFalse)
10771     {
10772       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10773         "  Setting up deflate compression");
10774 
10775       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10776         "    Compression buffer size: 32768");
10777     }
10778 
10779   png_set_compression_buffer_size(ping,32768L);
10780 
10781   if (logging != MagickFalse)
10782     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10783       "    Compression mem level: 9");
10784 
10785   png_set_compression_mem_level(ping, 9);
10786 
10787   /* Untangle the "-quality" setting:
10788 
10789      Undefined is 0; the default is used.
10790      Default is 75
10791 
10792      10's digit:
10793 
10794         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10795            zlib default compression level
10796 
10797         1-9: the zlib compression level
10798 
10799      1's digit:
10800 
10801         0-4: the PNG filter method
10802 
10803         5:   libpng adaptive filtering if compression level > 5
10804              libpng filter type "none" if compression level <= 5
10805                 or if image is grayscale or palette
10806 
10807         6:   libpng adaptive filtering
10808 
10809         7:   "LOCO" filtering (intrapixel differing) if writing
10810              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10811              and earlier because of a missing "else".
10812 
10813         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10814              filtering. Unused prior to IM-6.7.0-10, was same as 6
10815 
10816         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10817              Unused prior to IM-6.7.0-10, was same as 6
10818 
10819     Note that using the -quality option, not all combinations of
10820     PNG filter type, zlib compression level, and zlib compression
10821     strategy are possible.  This will be addressed soon in a
10822     release that accomodates "-define png:compression-strategy", etc.
10823 
10824    */
10825 
10826   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10827      image_info->quality;
10828 
10829   if (quality <= 9)
10830     {
10831       if (mng_info->write_png_compression_strategy == 0)
10832         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10833     }
10834 
10835   else if (mng_info->write_png_compression_level == 0)
10836     {
10837       int
10838         level;
10839 
10840       level=(int) MagickMin((ssize_t) quality/10,9);
10841 
10842       mng_info->write_png_compression_level = level+1;
10843     }
10844 
10845   if (mng_info->write_png_compression_strategy == 0)
10846     {
10847         if ((quality %10) == 8 || (quality %10) == 9)
10848 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10849           mng_info->write_png_compression_strategy=Z_RLE+1;
10850 #else
10851           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10852 #endif
10853     }
10854 
10855   if (mng_info->write_png_compression_filter == 0)
10856         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10857 
10858   if (logging != MagickFalse)
10859     {
10860         if (mng_info->write_png_compression_level)
10861           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10862             "    Compression level:    %d",
10863             (int) mng_info->write_png_compression_level-1);
10864 
10865         if (mng_info->write_png_compression_strategy)
10866           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10867             "    Compression strategy: %d",
10868             (int) mng_info->write_png_compression_strategy-1);
10869 
10870         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10871           "  Setting up filtering");
10872 
10873         if (mng_info->write_png_compression_filter == 6)
10874           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10875             "    Base filter method: ADAPTIVE");
10876         else if (mng_info->write_png_compression_filter == 0 ||
10877                  mng_info->write_png_compression_filter == 1)
10878           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10879             "    Base filter method: NONE");
10880         else
10881           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10882             "    Base filter method: %d",
10883             (int) mng_info->write_png_compression_filter-1);
10884     }
10885 
10886   if (mng_info->write_png_compression_level != 0)
10887     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10888 
10889   if (mng_info->write_png_compression_filter == 6)
10890     {
10891       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10892          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10893          (quality < 50))
10894         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10895       else
10896         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10897      }
10898   else if (mng_info->write_png_compression_filter == 7 ||
10899       mng_info->write_png_compression_filter == 10)
10900     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10901 
10902   else if (mng_info->write_png_compression_filter == 8)
10903     {
10904 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10905       if (mng_info->write_mng)
10906       {
10907          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10908              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10909         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10910       }
10911 #endif
10912       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10913     }
10914 
10915   else if (mng_info->write_png_compression_filter == 9)
10916     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10917 
10918   else if (mng_info->write_png_compression_filter != 0)
10919     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10920        mng_info->write_png_compression_filter-1);
10921 
10922   if (mng_info->write_png_compression_strategy != 0)
10923     png_set_compression_strategy(ping,
10924        mng_info->write_png_compression_strategy-1);
10925 
10926   ping_interlace_method=image_info->interlace != NoInterlace;
10927 
10928   if (mng_info->write_mng)
10929     png_set_sig_bytes(ping,8);
10930 
10931   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10932 
10933   if (mng_info->write_png_colortype != 0)
10934     {
10935      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10936        if (ping_have_color != MagickFalse)
10937          {
10938            ping_color_type = PNG_COLOR_TYPE_RGB;
10939 
10940            if (ping_bit_depth < 8)
10941              ping_bit_depth=8;
10942          }
10943 
10944      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10945        if (ping_have_color != MagickFalse)
10946          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10947     }
10948 
10949   if (ping_need_colortype_warning != MagickFalse ||
10950      ((mng_info->write_png_depth &&
10951      (int) mng_info->write_png_depth != ping_bit_depth) ||
10952      (mng_info->write_png_colortype &&
10953      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10954       mng_info->write_png_colortype != 7 &&
10955       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10956     {
10957       if (logging != MagickFalse)
10958         {
10959           if (ping_need_colortype_warning != MagickFalse)
10960             {
10961               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10962                  "  Image has transparency but tRNS chunk was excluded");
10963             }
10964 
10965           if (mng_info->write_png_depth)
10966             {
10967               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10968                   "  Defined png:bit-depth=%u, Computed depth=%u",
10969                   mng_info->write_png_depth,
10970                   ping_bit_depth);
10971             }
10972 
10973           if (mng_info->write_png_colortype)
10974             {
10975               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10976                   "  Defined png:color-type=%u, Computed color type=%u",
10977                   mng_info->write_png_colortype-1,
10978                   ping_color_type);
10979             }
10980         }
10981 
10982       png_warning(ping,
10983         "Cannot write image with defined png:bit-depth or png:color-type.");
10984     }
10985 
10986   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10987     {
10988       /* Add an opaque matte channel */
10989       image->alpha_trait = BlendPixelTrait;
10990       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10991 
10992       if (logging != MagickFalse)
10993         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10994           "  Added an opaque matte channel");
10995     }
10996 
10997   if (number_transparent != 0 || number_semitransparent != 0)
10998     {
10999       if (ping_color_type < 4)
11000         {
11001            ping_have_tRNS=MagickTrue;
11002            if (logging != MagickFalse)
11003              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11004                "  Setting ping_have_tRNS=MagickTrue.");
11005         }
11006     }
11007 
11008   if (logging != MagickFalse)
11009     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11010       "  Writing PNG header chunks");
11011 
11012   png_set_IHDR(ping,ping_info,ping_width,ping_height,
11013                ping_bit_depth,ping_color_type,
11014                ping_interlace_method,ping_compression_method,
11015                ping_filter_method);
11016 
11017   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
11018     {
11019       png_set_PLTE(ping,ping_info,palette,number_colors);
11020 
11021       if (logging != MagickFalse)
11022         {
11023           for (i=0; i< (ssize_t) number_colors; i++)
11024           {
11025             if (i < ping_num_trans)
11026               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11027                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
11028                       (int) i,
11029                       (int) palette[i].red,
11030                       (int) palette[i].green,
11031                       (int) palette[i].blue,
11032                       (int) i,
11033                       (int) ping_trans_alpha[i]);
11034              else
11035               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11036                 "     PLTE[%d] = (%d,%d,%d)",
11037                       (int) i,
11038                       (int) palette[i].red,
11039                       (int) palette[i].green,
11040                       (int) palette[i].blue);
11041            }
11042          }
11043     }
11044 
11045   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
11046   if (ping_exclude_sRGB != MagickFalse ||
11047      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11048   {
11049     if ((ping_exclude_tEXt == MagickFalse ||
11050        ping_exclude_zTXt == MagickFalse) &&
11051        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
11052     {
11053       ResetImageProfileIterator(image);
11054       for (name=GetNextImageProfile(image); name != (char *) NULL; )
11055       {
11056         profile=GetImageProfile(image,name);
11057 
11058         if (profile != (StringInfo *) NULL)
11059           {
11060 #ifdef PNG_WRITE_iCCP_SUPPORTED
11061             if ((LocaleCompare(name,"ICC") == 0) ||
11062                 (LocaleCompare(name,"ICM") == 0))
11063               {
11064                 ping_have_iCCP = MagickTrue;
11065                 if (ping_exclude_iCCP == MagickFalse)
11066                   {
11067                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11068                         "  Setting up iCCP chunk");
11069 
11070                     png_set_iCCP(ping,ping_info,(png_charp) name,0,
11071 #if (PNG_LIBPNG_VER < 10500)
11072                     (png_charp) GetStringInfoDatum(profile),
11073 #else
11074                     (const png_byte *) GetStringInfoDatum(profile),
11075 #endif
11076                     (png_uint_32) GetStringInfoLength(profile));
11077                   }
11078                 else
11079                   {
11080                     /* Do not write hex-encoded ICC chunk */
11081                        name=GetNextImageProfile(image);
11082                        continue;
11083                   }
11084               }
11085 #endif /* WRITE_iCCP */
11086 
11087             if (LocaleCompare(name,"exif") == 0)
11088               {
11089                    /* Do not write hex-encoded ICC chunk; we will
11090                       write it later as an eXIf chunk */
11091                    name=GetNextImageProfile(image);
11092                    continue;
11093               }
11094 
11095               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11096                  "  Setting up zTXt chunk with uuencoded %s profile",
11097                  name);
11098               Magick_png_write_raw_profile(image_info,ping,ping_info,
11099                 (unsigned char *) name,(unsigned char *) name,
11100                 GetStringInfoDatum(profile),(png_uint_32)
11101                 GetStringInfoLength(profile),exception);
11102           }
11103         name=GetNextImageProfile(image);
11104       }
11105     }
11106   }
11107 
11108 #if defined(PNG_WRITE_sRGB_SUPPORTED)
11109   if ((mng_info->have_write_global_srgb == 0) &&
11110       ping_have_iCCP != MagickTrue &&
11111       (ping_have_sRGB != MagickFalse ||
11112       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11113     {
11114       if (ping_exclude_sRGB == MagickFalse)
11115         {
11116           /*
11117             Note image rendering intent.
11118           */
11119           if (logging != MagickFalse)
11120             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11121                 "  Setting up sRGB chunk");
11122 
11123           (void) png_set_sRGB(ping,ping_info,(
11124             Magick_RenderingIntent_to_PNG_RenderingIntent(
11125               image->rendering_intent)));
11126 
11127           ping_have_sRGB = MagickTrue;
11128         }
11129     }
11130 
11131   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11132 #endif
11133     {
11134       if (ping_exclude_gAMA == MagickFalse &&
11135           ping_have_iCCP == MagickFalse &&
11136           ping_have_sRGB == MagickFalse &&
11137           (ping_exclude_sRGB == MagickFalse ||
11138           (image->gamma < .45 || image->gamma > .46)))
11139       {
11140       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11141         {
11142           /*
11143             Note image gamma.
11144             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11145           */
11146           if (logging != MagickFalse)
11147             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11148               "  Setting up gAMA chunk");
11149 
11150           png_set_gAMA(ping,ping_info,image->gamma);
11151         }
11152       }
11153 
11154       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11155         {
11156           if ((mng_info->have_write_global_chrm == 0) &&
11157               (image->chromaticity.red_primary.x != 0.0))
11158             {
11159               /*
11160                 Note image chromaticity.
11161                 Note: if cHRM+gAMA == sRGB write sRGB instead.
11162               */
11163                PrimaryInfo
11164                  bp,
11165                  gp,
11166                  rp,
11167                  wp;
11168 
11169                wp=image->chromaticity.white_point;
11170                rp=image->chromaticity.red_primary;
11171                gp=image->chromaticity.green_primary;
11172                bp=image->chromaticity.blue_primary;
11173 
11174                if (logging != MagickFalse)
11175                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11176                    "  Setting up cHRM chunk");
11177 
11178                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11179                    bp.x,bp.y);
11180            }
11181         }
11182     }
11183 
11184   if (ping_exclude_bKGD == MagickFalse)
11185     {
11186       if (ping_have_bKGD != MagickFalse)
11187         {
11188           png_set_bKGD(ping,ping_info,&ping_background);
11189           if (logging != MagickFalse)
11190             {
11191               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11192                    "    Setting up bKGD chunk");
11193               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11194                    "      background color = (%d,%d,%d)",
11195                         (int) ping_background.red,
11196                         (int) ping_background.green,
11197                         (int) ping_background.blue);
11198               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11199                    "      index = %d, gray=%d",
11200                         (int) ping_background.index,
11201                         (int) ping_background.gray);
11202             }
11203          }
11204     }
11205 
11206   if (ping_exclude_pHYs == MagickFalse)
11207     {
11208       if (ping_have_pHYs != MagickFalse)
11209         {
11210           png_set_pHYs(ping,ping_info,
11211              ping_pHYs_x_resolution,
11212              ping_pHYs_y_resolution,
11213              ping_pHYs_unit_type);
11214 
11215           if (logging != MagickFalse)
11216             {
11217               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11218                    "    Setting up pHYs chunk");
11219               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11220                    "      x_resolution=%lu",
11221                    (unsigned long) ping_pHYs_x_resolution);
11222               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11223                    "      y_resolution=%lu",
11224                    (unsigned long) ping_pHYs_y_resolution);
11225               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11226                    "      unit_type=%lu",
11227                    (unsigned long) ping_pHYs_unit_type);
11228             }
11229         }
11230     }
11231 
11232 #if defined(PNG_tIME_SUPPORTED)
11233   if (ping_exclude_tIME == MagickFalse)
11234     {
11235       const char
11236         *timestamp;
11237 
11238       if (image->taint == MagickFalse)
11239         {
11240           timestamp=GetImageOption(image_info,"png:tIME");
11241 
11242           if (timestamp == (const char *) NULL)
11243             timestamp=GetImageProperty(image,"png:tIME",exception);
11244         }
11245 
11246       else
11247         {
11248           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11249              "  Reset tIME in tainted image");
11250 
11251           timestamp=GetImageProperty(image,"date:modify",exception);
11252         }
11253 
11254       if (timestamp != (const char *) NULL)
11255           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11256     }
11257 #endif
11258 
11259   if (mng_info->need_blob != MagickFalse)
11260   {
11261     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11262        MagickFalse)
11263        png_error(ping,"WriteBlob Failed");
11264 
11265      ping_have_blob=MagickTrue;
11266   }
11267 
11268   png_write_info_before_PLTE(ping, ping_info);
11269 
11270   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11271     {
11272       if (logging != MagickFalse)
11273         {
11274           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11275               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11276         }
11277 
11278       if (ping_color_type == 3)
11279          (void) png_set_tRNS(ping, ping_info,
11280                 ping_trans_alpha,
11281                 ping_num_trans,
11282                 NULL);
11283 
11284       else
11285         {
11286            (void) png_set_tRNS(ping, ping_info,
11287                   NULL,
11288                   0,
11289                   &ping_trans_color);
11290 
11291            if (logging != MagickFalse)
11292              {
11293                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11294                  "     tRNS color   =(%d,%d,%d)",
11295                        (int) ping_trans_color.red,
11296                        (int) ping_trans_color.green,
11297                        (int) ping_trans_color.blue);
11298              }
11299          }
11300     }
11301 
11302   png_write_info(ping,ping_info);
11303 
11304   /* write orNT if image->orientation is defined */
11305   if (image->orientation != UndefinedOrientation)
11306     {
11307       unsigned char
11308         chunk[6];
11309       (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11310       PNGType(chunk,mng_orNT);
11311       LogPNGChunk(logging,mng_orNT,1L);
11312       /* PNG uses Exif orientation values */
11313       chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11314       (void) WriteBlob(image,5,chunk);
11315       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11316     }
11317 
11318   ping_wrote_caNv = MagickFalse;
11319 
11320   /* write caNv chunk */
11321   if (ping_exclude_caNv == MagickFalse)
11322     {
11323       if ((image->page.width != 0 && image->page.width != image->columns) ||
11324           (image->page.height != 0 && image->page.height != image->rows) ||
11325           image->page.x != 0 || image->page.y != 0)
11326         {
11327           unsigned char
11328             chunk[20];
11329 
11330           (void) WriteBlobMSBULong(image,16L);  /* data length=8 */
11331           PNGType(chunk,mng_caNv);
11332           LogPNGChunk(logging,mng_caNv,16L);
11333           PNGLong(chunk+4,(png_uint_32) image->page.width);
11334           PNGLong(chunk+8,(png_uint_32) image->page.height);
11335           PNGsLong(chunk+12,(png_int_32) image->page.x);
11336           PNGsLong(chunk+16,(png_int_32) image->page.y);
11337           (void) WriteBlob(image,20,chunk);
11338           (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11339           ping_wrote_caNv = MagickTrue;
11340         }
11341     }
11342 
11343 #if defined(PNG_oFFs_SUPPORTED)
11344   if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11345     {
11346       if (image->page.x || image->page.y)
11347         {
11348            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11349               (png_int_32) image->page.y, 0);
11350 
11351            if (logging != MagickFalse)
11352              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11353                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
11354                  (int) image->page.x, (int) image->page.y);
11355         }
11356     }
11357 #endif
11358 
11359 #if (PNG_LIBPNG_VER == 10206)
11360     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11361 #define PNG_HAVE_IDAT               0x04
11362     ping->mode |= PNG_HAVE_IDAT;
11363 #undef PNG_HAVE_IDAT
11364 #endif
11365 
11366   png_set_packing(ping);
11367   /*
11368     Allocate memory.
11369   */
11370   rowbytes=image->columns;
11371   if (image_depth > 8)
11372     rowbytes*=2;
11373   switch (ping_color_type)
11374     {
11375       case PNG_COLOR_TYPE_RGB:
11376         rowbytes*=3;
11377         break;
11378 
11379       case PNG_COLOR_TYPE_GRAY_ALPHA:
11380         rowbytes*=2;
11381         break;
11382 
11383       case PNG_COLOR_TYPE_RGBA:
11384         rowbytes*=4;
11385         break;
11386 
11387       default:
11388         break;
11389     }
11390 
11391   if (logging != MagickFalse)
11392     {
11393       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11394         "  Writing PNG image data");
11395 
11396       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11397         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11398     }
11399   pixel_info=AcquireVirtualMemory(rowbytes,GetPixelChannels(image)*
11400     sizeof(*ping_pixels));
11401   if (pixel_info == (MemoryInfo *) NULL)
11402     png_error(ping,"Allocation of memory for pixels failed");
11403   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11404   (void) memset(ping_pixels,0,rowbytes*GetPixelChannels(image)*
11405     sizeof(*ping_pixels));
11406   /*
11407     Initialize image scanlines.
11408   */
11409   quantum_info=AcquireQuantumInfo(image_info,image);
11410   if (quantum_info == (QuantumInfo *) NULL)
11411     png_error(ping,"Memory allocation for quantum_info failed");
11412   quantum_info->format=UndefinedQuantumFormat;
11413   SetQuantumDepth(image,quantum_info,image_depth);
11414   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11415   num_passes=png_set_interlace_handling(ping);
11416 
11417   if ((mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE) ||
11418       ((!mng_info->write_png8 && !mng_info->write_png24 &&
11419         !mng_info->write_png48 && !mng_info->write_png64 &&
11420         !mng_info->write_png32) &&
11421         (mng_info->IsPalette ||
11422         (image_info->type == BilevelType)) &&
11423         image_matte == MagickFalse &&
11424         ping_have_non_bw == MagickFalse))
11425      {
11426       /* Palette, Bilevel, or Opaque Monochrome */
11427       QuantumType
11428         quantum_type;
11429 
11430       const Quantum
11431         *p;
11432 
11433       quantum_type=RedQuantum;
11434       if (mng_info->IsPalette)
11435         {
11436           quantum_type=GrayQuantum;
11437           if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE)
11438             quantum_type=IndexQuantum;
11439         }
11440       SetQuantumDepth(image,quantum_info,8);
11441       for (pass=0; pass < num_passes; pass++)
11442       {
11443         /*
11444           Convert PseudoClass image to a PNG monochrome image.
11445         */
11446         for (y=0; y < (ssize_t) image->rows; y++)
11447         {
11448           if (logging != MagickFalse && y == 0)
11449              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11450                  "    Writing row of pixels (0)");
11451 
11452           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11453 
11454           if (p == (const Quantum *) NULL)
11455             break;
11456 
11457           (void) ExportQuantumPixels(image,(CacheView *) NULL,
11458             quantum_info,quantum_type,ping_pixels,exception);
11459           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11460             for (i=0; i < (ssize_t) image->columns; i++)
11461                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11462                       255 : 0);
11463 
11464           if (logging != MagickFalse && y == 0)
11465             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11466                 "    Writing row of pixels (1)");
11467 
11468           png_write_row(ping,ping_pixels);
11469 
11470           status=SetImageProgress(image,SaveImageTag,
11471               (MagickOffsetType) (pass * image->rows + y),
11472               num_passes * image->rows);
11473 
11474           if (status == MagickFalse)
11475             break;
11476         }
11477       }
11478     }
11479 
11480   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11481     {
11482       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11483           !mng_info->write_png48 && !mng_info->write_png64 &&
11484           !mng_info->write_png32) && (image_matte != MagickFalse ||
11485           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11486           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11487         {
11488           const Quantum
11489             *p;
11490 
11491           for (pass=0; pass < num_passes; pass++)
11492           {
11493 
11494           for (y=0; y < (ssize_t) image->rows; y++)
11495           {
11496             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11497 
11498             if (p == (const Quantum *) NULL)
11499               break;
11500 
11501             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11502               {
11503                 if (mng_info->IsPalette)
11504                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11505                     quantum_info,GrayQuantum,ping_pixels,exception);
11506 
11507                 else
11508                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11509                     quantum_info,RedQuantum,ping_pixels,exception);
11510 
11511                 if (logging != MagickFalse && y == 0)
11512                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11513                        "    Writing GRAY PNG pixels (2)");
11514               }
11515 
11516             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11517               {
11518                 if (logging != MagickFalse && y == 0)
11519                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11520                          "    Writing GRAY_ALPHA PNG pixels (2)");
11521 
11522                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11523                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11524               }
11525 
11526             if (logging != MagickFalse && y == 0)
11527               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11528                   "    Writing row of pixels (2)");
11529 
11530             png_write_row(ping,ping_pixels);
11531 
11532             status=SetImageProgress(image,SaveImageTag,
11533               (MagickOffsetType) (pass * image->rows + y),
11534               num_passes * image->rows);
11535 
11536             if (status == MagickFalse)
11537               break;
11538             }
11539           }
11540         }
11541 
11542       else
11543         {
11544           const Quantum
11545             *p;
11546 
11547           for (pass=0; pass < num_passes; pass++)
11548           {
11549             if ((image_depth > 8) ||
11550                 mng_info->write_png24 ||
11551                 mng_info->write_png32 ||
11552                 mng_info->write_png48 ||
11553                 mng_info->write_png64 ||
11554                 (!mng_info->write_png8 && !mng_info->IsPalette))
11555             {
11556               for (y=0; y < (ssize_t) image->rows; y++)
11557               {
11558                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11559 
11560                 if (p == (const Quantum *) NULL)
11561                   break;
11562 
11563                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11564                   {
11565                     if (image->storage_class == DirectClass)
11566                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11567                         quantum_info,RedQuantum,ping_pixels,exception);
11568 
11569                     else
11570                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11571                         quantum_info,GrayQuantum,ping_pixels,exception);
11572                   }
11573 
11574                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11575                   {
11576                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11577                       quantum_info,GrayAlphaQuantum,ping_pixels,
11578                       exception);
11579 
11580                     if (logging != MagickFalse && y == 0)
11581                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582                            "    Writing GRAY_ALPHA PNG pixels (3)");
11583                   }
11584 
11585                 else if (image_matte != MagickFalse)
11586                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11587                     quantum_info,RGBAQuantum,ping_pixels,exception);
11588 
11589                 else
11590                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11591                     quantum_info,RGBQuantum,ping_pixels,exception);
11592 
11593                 if (logging != MagickFalse && y == 0)
11594                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11595                       "    Writing row of pixels (3)");
11596 
11597                 png_write_row(ping,ping_pixels);
11598 
11599                 status=SetImageProgress(image,SaveImageTag,
11600                   (MagickOffsetType) (pass * image->rows + y),
11601                   num_passes * image->rows);
11602 
11603                 if (status == MagickFalse)
11604                   break;
11605               }
11606             }
11607 
11608           else
11609             /* not ((image_depth > 8) ||
11610                 mng_info->write_png24 || mng_info->write_png32 ||
11611                 mng_info->write_png48 || mng_info->write_png64 ||
11612                 (!mng_info->write_png8 && !mng_info->IsPalette))
11613              */
11614             {
11615               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11616                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11617                 {
11618                   if (logging != MagickFalse)
11619                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11620                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11621 
11622                   SetQuantumDepth(image,quantum_info,8);
11623                   image_depth=8;
11624                 }
11625 
11626               for (y=0; y < (ssize_t) image->rows; y++)
11627               {
11628                 if (logging != MagickFalse && y == 0)
11629                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11630                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11631                     pass);
11632 
11633                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11634 
11635                 if (p == (const Quantum *) NULL)
11636                   break;
11637 
11638                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11639                   {
11640                     SetQuantumDepth(image,quantum_info,image->depth);
11641 
11642                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11643                        quantum_info,GrayQuantum,ping_pixels,exception);
11644                   }
11645 
11646                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11647                   {
11648                     if (logging != MagickFalse && y == 0)
11649                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11650                            "  Writing GRAY_ALPHA PNG pixels (4)");
11651 
11652                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11653                          quantum_info,GrayAlphaQuantum,ping_pixels,
11654                          exception);
11655                   }
11656 
11657                 else
11658                   {
11659                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11660                       quantum_info,IndexQuantum,ping_pixels,exception);
11661 
11662                     if (logging != MagickFalse && y <= 2)
11663                     {
11664                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11665                           "  Writing row of non-gray pixels (4)");
11666 
11667                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11668                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11669                           (int)ping_pixels[0],(int)ping_pixels[1]);
11670                     }
11671                   }
11672                 png_write_row(ping,ping_pixels);
11673 
11674                 status=SetImageProgress(image,SaveImageTag,
11675                   (MagickOffsetType) (pass * image->rows + y),
11676                   num_passes * image->rows);
11677 
11678                 if (status == MagickFalse)
11679                   break;
11680               }
11681             }
11682           }
11683         }
11684     }
11685 
11686   if (quantum_info != (QuantumInfo *) NULL)
11687     quantum_info=DestroyQuantumInfo(quantum_info);
11688 
11689   if (logging != MagickFalse)
11690     {
11691       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11692         "  Wrote PNG image data");
11693 
11694       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11695         "    Width: %.20g",(double) ping_width);
11696 
11697       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11698         "    Height: %.20g",(double) ping_height);
11699 
11700       if (mng_info->write_png_depth)
11701         {
11702           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11703             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11704         }
11705 
11706       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11707         "    PNG bit-depth written: %d",ping_bit_depth);
11708 
11709       if (mng_info->write_png_colortype)
11710         {
11711           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11712             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11713         }
11714 
11715       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11716         "    PNG color-type written: %d",ping_color_type);
11717 
11718       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11719         "    PNG Interlace method: %d",ping_interlace_method);
11720     }
11721   /*
11722     Generate text chunks after IDAT.
11723   */
11724   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11725   {
11726     ResetImagePropertyIterator(image);
11727     property=GetNextImageProperty(image);
11728     while (property != (const char *) NULL)
11729     {
11730       png_textp
11731         text;
11732 
11733       value=GetImageProperty(image,property,exception);
11734 
11735       /* Don't write any "png:" or "jpeg:" properties; those are just for
11736        * "identify" or for passing through to another JPEG
11737        */
11738       if ((LocaleNCompare(property,"png:",4) != 0 &&
11739            LocaleNCompare(property,"jpeg:",5) != 0) &&
11740 
11741 
11742           /* Suppress density and units if we wrote a pHYs chunk */
11743           (ping_exclude_pHYs != MagickFalse      ||
11744           LocaleCompare(property,"density") != 0 ||
11745           LocaleCompare(property,"units") != 0) &&
11746 
11747           /* Suppress the IM-generated Date:create and Date:modify */
11748           (ping_exclude_date == MagickFalse      ||
11749           LocaleNCompare(property, "Date:",5) != 0))
11750         {
11751         if (value != (const char *) NULL)
11752           {
11753 
11754 #if PNG_LIBPNG_VER >= 10400
11755             text=(png_textp) png_malloc(ping,
11756                  (png_alloc_size_t) sizeof(png_text));
11757 #else
11758             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11759 #endif
11760             text[0].key=(char *) property;
11761             text[0].text=(char *) value;
11762             text[0].text_length=strlen(value);
11763 
11764             if (ping_exclude_tEXt != MagickFalse)
11765                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11766 
11767             else if (ping_exclude_zTXt != MagickFalse)
11768                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11769 
11770             else
11771             {
11772                text[0].compression=image_info->compression == NoCompression ||
11773                  (image_info->compression == UndefinedCompression &&
11774                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11775                  PNG_TEXT_COMPRESSION_zTXt ;
11776             }
11777 
11778             if (logging != MagickFalse)
11779               {
11780                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11781                   "  Setting up text chunk");
11782 
11783                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11784                   "    keyword: '%s'",text[0].key);
11785               }
11786 
11787             png_set_text(ping,ping_info,text,1);
11788             png_free(ping,text);
11789           }
11790         }
11791       property=GetNextImageProperty(image);
11792     }
11793   }
11794 
11795   /* write eXIf profile */
11796   if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11797     {
11798       ResetImageProfileIterator(image);
11799 
11800       for (name=GetNextImageProfile(image); name != (char *) NULL; )
11801       {
11802         if (LocaleCompare(name,"exif") == 0)
11803           {
11804             profile=GetImageProfile(image,name);
11805 
11806             if (profile != (StringInfo *) NULL)
11807               {
11808                 png_uint_32
11809                   length;
11810 
11811                 unsigned char
11812                   chunk[4],
11813                   *data;
11814 
11815                 StringInfo
11816                   *ping_profile;
11817 
11818                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11819                     "  Have eXIf profile");
11820 
11821                 ping_profile=CloneStringInfo(profile);
11822                 data=GetStringInfoDatum(ping_profile),
11823                 length=(png_uint_32) GetStringInfoLength(ping_profile);
11824 
11825                 PNGType(chunk,mng_eXIf);
11826                 if (length < 7)
11827                   {
11828                     ping_profile=DestroyStringInfo(ping_profile);
11829                     break;  /* otherwise crashes */
11830                   }
11831 
11832                 if (*data == 'E' && *(data+1) == 'x' && *(data+2) == 'i' &&
11833                     *(data+3) == 'f' && *(data+4) == '\0' && *(data+5) == '\0')
11834                   {
11835                     /* skip the "Exif\0\0" JFIF Exif Header ID */
11836                     length -= 6;
11837                     data += 6;
11838                   }
11839 
11840                 LogPNGChunk(logging,chunk,length);
11841                 (void) WriteBlobMSBULong(image,length);
11842                 (void) WriteBlob(image,4,chunk);
11843                 (void) WriteBlob(image,length,data);
11844                 (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4), data,
11845                   (uInt) length));
11846                 ping_profile=DestroyStringInfo(ping_profile);
11847                 break;
11848              }
11849          }
11850        name=GetNextImageProfile(image);
11851      }
11852   }
11853 
11854   if (logging != MagickFalse)
11855     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11856       "  Writing PNG end info");
11857 
11858   png_write_end(ping,ping_info);
11859 
11860   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11861     {
11862       if (mng_info->page.x || mng_info->page.y ||
11863           (ping_width != mng_info->page.width) ||
11864           (ping_height != mng_info->page.height))
11865         {
11866           unsigned char
11867             chunk[32];
11868 
11869           /*
11870             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11871           */
11872           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11873           PNGType(chunk,mng_FRAM);
11874           LogPNGChunk(logging,mng_FRAM,27L);
11875           chunk[4]=4;
11876           chunk[5]=0;  /* frame name separator (no name) */
11877           chunk[6]=1;  /* flag for changing delay, for next frame only */
11878           chunk[7]=0;  /* flag for changing frame timeout */
11879           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11880           chunk[9]=0;  /* flag for changing frame sync_id */
11881           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11882           chunk[14]=0; /* clipping boundaries delta type */
11883           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11884           PNGLong(chunk+19,
11885              (png_uint_32) (mng_info->page.x + ping_width));
11886           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11887           PNGLong(chunk+27,
11888              (png_uint_32) (mng_info->page.y + ping_height));
11889           (void) WriteBlob(image,31,chunk);
11890           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11891           mng_info->old_framing_mode=4;
11892           mng_info->framing_mode=1;
11893         }
11894 
11895       else
11896         mng_info->framing_mode=3;
11897     }
11898   if (mng_info->write_mng && !mng_info->need_fram &&
11899       ((int) image->dispose == 3))
11900      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11901 
11902   /*
11903     Free PNG resources.
11904   */
11905 
11906   png_destroy_write_struct(&ping,&ping_info);
11907 
11908   pixel_info=RelinquishVirtualMemory(pixel_info);
11909 
11910   if (ping_have_blob != MagickFalse)
11911      (void) CloseBlob(image);
11912 
11913   image_info=DestroyImageInfo(image_info);
11914   image=DestroyImage(image);
11915 
11916   /* Store bit depth actually written */
11917   s[0]=(char) ping_bit_depth;
11918   s[1]='\0';
11919 
11920   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11921 
11922   if (logging != MagickFalse)
11923     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11924       "  exit WriteOnePNGImage()");
11925 
11926 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11927   UnlockSemaphoreInfo(ping_semaphore);
11928 #endif
11929 
11930    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11931     *    Throwing an Exception when an error occurs.
11932     */
11933 
11934   return(MagickTrue);
11935 /*  End write one PNG image */
11936 
11937 }
11938 
11939 /*
11940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11941 %                                                                             %
11942 %                                                                             %
11943 %                                                                             %
11944 %   W r i t e P N G I m a g e                                                 %
11945 %                                                                             %
11946 %                                                                             %
11947 %                                                                             %
11948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11949 %
11950 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11951 %  Multiple-image Network Graphics (MNG) image file.
11952 %
11953 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11954 %
11955 %  The format of the WritePNGImage method is:
11956 %
11957 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11958 %        Image *image,ExceptionInfo *exception)
11959 %
11960 %  A description of each parameter follows:
11961 %
11962 %    o image_info: the image info.
11963 %
11964 %    o image:  The image.
11965 %
11966 %    o exception: return any errors or warnings in this structure.
11967 %
11968 %  Returns MagickTrue on success, MagickFalse on failure.
11969 %
11970 %  Communicating with the PNG encoder:
11971 %
11972 %  While the datastream written is always in PNG format and normally would
11973 %  be given the "png" file extension, this method also writes the following
11974 %  pseudo-formats which are subsets of png:
11975 %
11976 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11977 %               a depth greater than 8, the depth is reduced. If transparency
11978 %               is present, the tRNS chunk must only have values 0 and 255
11979 %               (i.e., transparency is binary: fully opaque or fully
11980 %               transparent).  If other values are present they will be
11981 %               50%-thresholded to binary transparency.  If more than 256
11982 %               colors are present, they will be quantized to the 4-4-4-1,
11983 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11984 %               of any resulting fully-transparent pixels is changed to
11985 %               the image's background color.
11986 %
11987 %               If you want better quantization or dithering of the colors
11988 %               or alpha than that, you need to do it before calling the
11989 %               PNG encoder. The pixels contain 8-bit indices even if
11990 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11991 %               images will be written as indexed PNG files even though the
11992 %               PNG grayscale type might be slightly more efficient.  Please
11993 %               note that writing to the PNG8 format may result in loss
11994 %               of color and alpha data.
11995 %
11996 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11997 %               chunk can be present to convey binary transparency by naming
11998 %               one of the colors as transparent.  The only loss incurred
11999 %               is reduction of sample depth to 8.  If the image has more
12000 %               than one transparent color, has semitransparent pixels, or
12001 %               has an opaque pixel with the same RGB components as the
12002 %               transparent color, an image is not written.
12003 %
12004 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
12005 %               transparency is permitted, i.e., the alpha sample for
12006 %               each pixel can have any value from 0 to 255. The alpha
12007 %               channel is present even if the image is fully opaque.
12008 %               The only loss in data is the reduction of the sample depth
12009 %               to 8.
12010 %
12011 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
12012 %               chunk can be present to convey binary transparency by naming
12013 %               one of the colors as transparent.  If the image has more
12014 %               than one transparent color, has semitransparent pixels, or
12015 %               has an opaque pixel with the same RGB components as the
12016 %               transparent color, an image is not written.
12017 %
12018 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
12019 %               transparency is permitted, i.e., the alpha sample for
12020 %               each pixel can have any value from 0 to 65535. The alpha
12021 %               channel is present even if the image is fully opaque.
12022 %
12023 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
12024 %               image, if the input was a PNG, is written.  If these values
12025 %               cannot be found, or if the pixels have been changed in a way
12026 %               that makes this impossible, then "PNG00" falls back to the
12027 %               regular "PNG" format.
12028 %
12029 %    o -define: For more precise control of the PNG output, you can use the
12030 %               Image options "png:bit-depth" and "png:color-type".  These
12031 %               can be set from the commandline with "-define" and also
12032 %               from the application programming interfaces.  The options
12033 %               are case-independent and are converted to lowercase before
12034 %               being passed to this encoder.
12035 %
12036 %               png:color-type can be 0, 2, 3, 4, or 6.
12037 %
12038 %               When png:color-type is 0 (Grayscale), png:bit-depth can
12039 %               be 1, 2, 4, 8, or 16.
12040 %
12041 %               When png:color-type is 2 (RGB), png:bit-depth can
12042 %               be 8 or 16.
12043 %
12044 %               When png:color-type is 3 (Indexed), png:bit-depth can
12045 %               be 1, 2, 4, or 8.  This refers to the number of bits
12046 %               used to store the index.  The color samples always have
12047 %               bit-depth 8 in indexed PNG files.
12048 %
12049 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
12050 %               png:bit-depth can be 8 or 16.
12051 %
12052 %               If the image cannot be written without loss with the
12053 %               requested bit-depth and color-type, a PNG file will not
12054 %               be written, a warning will be issued, and the encoder will
12055 %               return MagickFalse.
12056 %
12057 %  Since image encoders should not be responsible for the "heavy lifting",
12058 %  the user should make sure that ImageMagick has already reduced the
12059 %  image depth and number of colors and limit transparency to binary
12060 %  transparency prior to attempting to write the image with depth, color,
12061 %  or transparency limitations.
12062 %
12063 %  Note that another definition, "png:bit-depth-written" exists, but it
12064 %  is not intended for external use.  It is only used internally by the
12065 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
12066 %
12067 %  As of version 6.6.6 the following optimizations are always done:
12068 %
12069 %   o  32-bit depth is reduced to 16.
12070 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
12071 %      high byte and low byte are identical.
12072 %   o  Palette is sorted to remove unused entries and to put a
12073 %      transparent color first, if BUILD_PNG_PALETTE is defined.
12074 %   o  Opaque matte channel is removed when writing an indexed PNG.
12075 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
12076 %      this can be done without loss and a larger bit depth N was not
12077 %      requested via the "-define png:bit-depth=N" option.
12078 %   o  If matte channel is present but only one transparent color is
12079 %      present, RGB+tRNS is written instead of RGBA
12080 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
12081 %      was requested when converting an opaque image).
12082 %
12083 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12084 */
WritePNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12085 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
12086   Image *image,ExceptionInfo *exception)
12087 {
12088   MagickBooleanType
12089     excluding,
12090     logging,
12091     status;
12092 
12093   MngInfo
12094     *mng_info;
12095 
12096   const char
12097     *value;
12098 
12099   int
12100     source;
12101 
12102   /*
12103     Open image file.
12104   */
12105   assert(image_info != (const ImageInfo *) NULL);
12106   assert(image_info->signature == MagickCoreSignature);
12107   assert(image != (Image *) NULL);
12108   assert(image->signature == MagickCoreSignature);
12109   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12110   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
12111   /*
12112     Allocate a MngInfo structure.
12113   */
12114   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12115 
12116   if (mng_info == (MngInfo *) NULL)
12117     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12118 
12119   /*
12120     Initialize members of the MngInfo structure.
12121   */
12122   (void) memset(mng_info,0,sizeof(MngInfo));
12123   mng_info->image=image;
12124   mng_info->equal_backgrounds=MagickTrue;
12125 
12126   /* See if user has requested a specific PNG subformat */
12127 
12128   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12129   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12130   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12131   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12132   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12133 
12134   value=GetImageOption(image_info,"png:format");
12135 
12136   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12137     {
12138       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12139          "  Format=%s",value);
12140 
12141       mng_info->write_png8 = MagickFalse;
12142       mng_info->write_png24 = MagickFalse;
12143       mng_info->write_png32 = MagickFalse;
12144       mng_info->write_png48 = MagickFalse;
12145       mng_info->write_png64 = MagickFalse;
12146 
12147       if (LocaleCompare(value,"png8") == 0)
12148         mng_info->write_png8 = MagickTrue;
12149 
12150       else if (LocaleCompare(value,"png24") == 0)
12151         mng_info->write_png24 = MagickTrue;
12152 
12153       else if (LocaleCompare(value,"png32") == 0)
12154         mng_info->write_png32 = MagickTrue;
12155 
12156       else if (LocaleCompare(value,"png48") == 0)
12157         mng_info->write_png48 = MagickTrue;
12158 
12159       else if (LocaleCompare(value,"png64") == 0)
12160         mng_info->write_png64 = MagickTrue;
12161 
12162       else if ((LocaleCompare(value,"png00") == 0) ||
12163          LocaleCompare(image_info->magick,"PNG00") == 0)
12164         {
12165           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12166           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12167 
12168           if (value != (char *) NULL)
12169             {
12170               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12171                  "  png00 inherited bit depth=%s",value);
12172 
12173               if (LocaleCompare(value,"1") == 0)
12174                 mng_info->write_png_depth = 1;
12175 
12176               else if (LocaleCompare(value,"2") == 0)
12177                 mng_info->write_png_depth = 2;
12178 
12179               else if (LocaleCompare(value,"4") == 0)
12180                 mng_info->write_png_depth = 4;
12181 
12182               else if (LocaleCompare(value,"8") == 0)
12183                 mng_info->write_png_depth = 8;
12184 
12185               else if (LocaleCompare(value,"16") == 0)
12186                 mng_info->write_png_depth = 16;
12187             }
12188 
12189           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12190 
12191           if (value != (char *) NULL)
12192             {
12193               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12194                  "  png00 inherited color type=%s",value);
12195 
12196               if (LocaleCompare(value,"0") == 0)
12197                 mng_info->write_png_colortype = 1;
12198 
12199               else if (LocaleCompare(value,"2") == 0)
12200                 mng_info->write_png_colortype = 3;
12201 
12202               else if (LocaleCompare(value,"3") == 0)
12203                 mng_info->write_png_colortype = 4;
12204 
12205               else if (LocaleCompare(value,"4") == 0)
12206                 mng_info->write_png_colortype = 5;
12207 
12208               else if (LocaleCompare(value,"6") == 0)
12209                 mng_info->write_png_colortype = 7;
12210             }
12211         }
12212     }
12213 
12214   if (mng_info->write_png8)
12215     {
12216       mng_info->write_png_colortype = /* 3 */ 4;
12217       mng_info->write_png_depth = 8;
12218       image->depth = 8;
12219     }
12220 
12221   if (mng_info->write_png24)
12222     {
12223       mng_info->write_png_colortype = /* 2 */ 3;
12224       mng_info->write_png_depth = 8;
12225       image->depth = 8;
12226 
12227       if (image->alpha_trait != UndefinedPixelTrait)
12228         (void) SetImageType(image,TrueColorAlphaType,exception);
12229 
12230       else
12231         (void) SetImageType(image,TrueColorType,exception);
12232 
12233       (void) SyncImage(image,exception);
12234     }
12235 
12236   if (mng_info->write_png32)
12237     {
12238       mng_info->write_png_colortype = /* 6 */  7;
12239       mng_info->write_png_depth = 8;
12240       image->depth = 8;
12241       image->alpha_trait = BlendPixelTrait;
12242 
12243       (void) SetImageType(image,TrueColorAlphaType,exception);
12244       (void) SyncImage(image,exception);
12245     }
12246 
12247   if (mng_info->write_png48)
12248     {
12249       mng_info->write_png_colortype = /* 2 */ 3;
12250       mng_info->write_png_depth = 16;
12251       image->depth = 16;
12252 
12253       if (image->alpha_trait != UndefinedPixelTrait)
12254         (void) SetImageType(image,TrueColorAlphaType,exception);
12255 
12256       else
12257         (void) SetImageType(image,TrueColorType,exception);
12258 
12259       (void) SyncImage(image,exception);
12260     }
12261 
12262   if (mng_info->write_png64)
12263     {
12264       mng_info->write_png_colortype = /* 6 */  7;
12265       mng_info->write_png_depth = 16;
12266       image->depth = 16;
12267       image->alpha_trait = BlendPixelTrait;
12268 
12269       (void) SetImageType(image,TrueColorAlphaType,exception);
12270       (void) SyncImage(image,exception);
12271     }
12272 
12273   value=GetImageOption(image_info,"png:bit-depth");
12274 
12275   if (value != (char *) NULL)
12276     {
12277       if (LocaleCompare(value,"1") == 0)
12278         mng_info->write_png_depth = 1;
12279 
12280       else if (LocaleCompare(value,"2") == 0)
12281         mng_info->write_png_depth = 2;
12282 
12283       else if (LocaleCompare(value,"4") == 0)
12284         mng_info->write_png_depth = 4;
12285 
12286       else if (LocaleCompare(value,"8") == 0)
12287         mng_info->write_png_depth = 8;
12288 
12289       else if (LocaleCompare(value,"16") == 0)
12290         mng_info->write_png_depth = 16;
12291 
12292       else
12293         (void) ThrowMagickException(exception,
12294              GetMagickModule(),CoderWarning,
12295              "ignoring invalid defined png:bit-depth",
12296              "=%s",value);
12297 
12298       if (logging != MagickFalse)
12299         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12300           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12301     }
12302 
12303   value=GetImageOption(image_info,"png:color-type");
12304 
12305   if (value != (char *) NULL)
12306     {
12307       /* We must store colortype+1 because 0 is a valid colortype */
12308       if (LocaleCompare(value,"0") == 0)
12309         mng_info->write_png_colortype = 1;
12310 
12311       else if (LocaleCompare(value,"1") == 0)
12312         mng_info->write_png_colortype = 2;
12313 
12314       else if (LocaleCompare(value,"2") == 0)
12315         mng_info->write_png_colortype = 3;
12316 
12317       else if (LocaleCompare(value,"3") == 0)
12318         mng_info->write_png_colortype = 4;
12319 
12320       else if (LocaleCompare(value,"4") == 0)
12321         mng_info->write_png_colortype = 5;
12322 
12323       else if (LocaleCompare(value,"6") == 0)
12324         mng_info->write_png_colortype = 7;
12325 
12326       else
12327         (void) ThrowMagickException(exception,
12328              GetMagickModule(),CoderWarning,
12329              "ignoring invalid defined png:color-type",
12330              "=%s",value);
12331 
12332       if (logging != MagickFalse)
12333         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12334           "  png:color-type=%d was defined.\n",
12335           mng_info->write_png_colortype-1);
12336     }
12337 
12338   /* Check for chunks to be excluded:
12339    *
12340    * The default is to not exclude any known chunks except for any
12341    * listed in the "unused_chunks" array, above.
12342    *
12343    * Chunks can be listed for exclusion via a "png:exclude-chunk"
12344    * define (in the image properties or in the image artifacts)
12345    * or via a mng_info member.  For convenience, in addition
12346    * to or instead of a comma-separated list of chunks, the
12347    * "exclude-chunk" string can be simply "all" or "none".
12348    *
12349    * Note that the "-strip" option provides a convenient way of
12350    * doing the equivalent of
12351    *
12352    *    -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12353    *            iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12354    *
12355    * The exclude-chunk define takes priority over the mng_info.
12356    *
12357    * A "png:include-chunk" define takes  priority over both the
12358    * mng_info and the "png:exclude-chunk" define.  Like the
12359    * "exclude-chunk" string, it can define "all" or "none" as
12360    * well as a comma-separated list.  Chunks that are unknown to
12361    * ImageMagick are always excluded, regardless of their "copy-safe"
12362    * status according to the PNG specification, and even if they
12363    * appear in the "include-chunk" list. Such defines appearing among
12364    * the image options take priority over those found among the image
12365    * artifacts.
12366    *
12367    * Finally, all chunks listed in the "unused_chunks" array are
12368    * automatically excluded, regardless of the other instructions
12369    * or lack thereof.
12370    *
12371    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12372    * will not be written and the gAMA chunk will only be written if it
12373    * is not between .45 and .46, or approximately (1.0/2.2).
12374    *
12375    * If you exclude tRNS and the image has transparency, the colortype
12376    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12377    *
12378    * The -strip option causes StripImage() to set the png:include-chunk
12379    * artifact to "none,trns,gama".
12380    */
12381 
12382   mng_info->ping_exclude_bKGD=MagickFalse;
12383   mng_info->ping_exclude_caNv=MagickFalse;
12384   mng_info->ping_exclude_cHRM=MagickFalse;
12385   mng_info->ping_exclude_date=MagickFalse;
12386   mng_info->ping_exclude_eXIf=MagickFalse;
12387   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12388   mng_info->ping_exclude_gAMA=MagickFalse;
12389   mng_info->ping_exclude_iCCP=MagickFalse;
12390   /* mng_info->ping_exclude_iTXt=MagickFalse; */
12391   mng_info->ping_exclude_oFFs=MagickFalse;
12392   mng_info->ping_exclude_pHYs=MagickFalse;
12393   mng_info->ping_exclude_sRGB=MagickFalse;
12394   mng_info->ping_exclude_tEXt=MagickFalse;
12395   mng_info->ping_exclude_tIME=MagickFalse;
12396   mng_info->ping_exclude_tRNS=MagickFalse;
12397   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12398   mng_info->ping_exclude_zTXt=MagickFalse;
12399 
12400   mng_info->ping_preserve_colormap=MagickFalse;
12401 
12402   value=GetImageOption(image_info,"png:preserve-colormap");
12403   if (value == NULL)
12404      value=GetImageArtifact(image,"png:preserve-colormap");
12405   if (value != NULL)
12406      mng_info->ping_preserve_colormap=MagickTrue;
12407 
12408   mng_info->ping_preserve_iCCP=MagickFalse;
12409 
12410   value=GetImageOption(image_info,"png:preserve-iCCP");
12411   if (value == NULL)
12412      value=GetImageArtifact(image,"png:preserve-iCCP");
12413   if (value != NULL)
12414      mng_info->ping_preserve_iCCP=MagickTrue;
12415 
12416   /* These compression-level, compression-strategy, and compression-filter
12417    * defines take precedence over values from the -quality option.
12418    */
12419   value=GetImageOption(image_info,"png:compression-level");
12420   if (value == NULL)
12421      value=GetImageArtifact(image,"png:compression-level");
12422   if (value != NULL)
12423   {
12424       /* We have to add 1 to everything because 0 is a valid input,
12425        * and we want to use 0 (the default) to mean undefined.
12426        */
12427       if (LocaleCompare(value,"0") == 0)
12428         mng_info->write_png_compression_level = 1;
12429 
12430       else if (LocaleCompare(value,"1") == 0)
12431         mng_info->write_png_compression_level = 2;
12432 
12433       else if (LocaleCompare(value,"2") == 0)
12434         mng_info->write_png_compression_level = 3;
12435 
12436       else if (LocaleCompare(value,"3") == 0)
12437         mng_info->write_png_compression_level = 4;
12438 
12439       else if (LocaleCompare(value,"4") == 0)
12440         mng_info->write_png_compression_level = 5;
12441 
12442       else if (LocaleCompare(value,"5") == 0)
12443         mng_info->write_png_compression_level = 6;
12444 
12445       else if (LocaleCompare(value,"6") == 0)
12446         mng_info->write_png_compression_level = 7;
12447 
12448       else if (LocaleCompare(value,"7") == 0)
12449         mng_info->write_png_compression_level = 8;
12450 
12451       else if (LocaleCompare(value,"8") == 0)
12452         mng_info->write_png_compression_level = 9;
12453 
12454       else if (LocaleCompare(value,"9") == 0)
12455         mng_info->write_png_compression_level = 10;
12456 
12457       else
12458         (void) ThrowMagickException(exception,
12459              GetMagickModule(),CoderWarning,
12460              "ignoring invalid defined png:compression-level",
12461              "=%s",value);
12462     }
12463 
12464   value=GetImageOption(image_info,"png:compression-strategy");
12465   if (value == NULL)
12466      value=GetImageArtifact(image,"png:compression-strategy");
12467   if (value != NULL)
12468   {
12469       if (LocaleCompare(value,"0") == 0)
12470         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12471 
12472       else if (LocaleCompare(value,"1") == 0)
12473         mng_info->write_png_compression_strategy = Z_FILTERED+1;
12474 
12475       else if (LocaleCompare(value,"2") == 0)
12476         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12477 
12478       else if (LocaleCompare(value,"3") == 0)
12479 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
12480         mng_info->write_png_compression_strategy = Z_RLE+1;
12481 #else
12482         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12483 #endif
12484 
12485       else if (LocaleCompare(value,"4") == 0)
12486 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
12487         mng_info->write_png_compression_strategy = Z_FIXED+1;
12488 #else
12489         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12490 #endif
12491 
12492       else
12493         (void) ThrowMagickException(exception,
12494              GetMagickModule(),CoderWarning,
12495              "ignoring invalid defined png:compression-strategy",
12496              "=%s",value);
12497     }
12498 
12499   value=GetImageOption(image_info,"png:compression-filter");
12500   if (value == NULL)
12501      value=GetImageArtifact(image,"png:compression-filter");
12502   if (value != NULL)
12503   {
12504       /* To do: combinations of filters allowed by libpng
12505        * masks 0x08 through 0xf8
12506        *
12507        * Implement this as a comma-separated list of 0,1,2,3,4,5
12508        * where 5 is a special case meaning PNG_ALL_FILTERS.
12509        */
12510 
12511       if (LocaleCompare(value,"0") == 0)
12512         mng_info->write_png_compression_filter = 1;
12513 
12514       else if (LocaleCompare(value,"1") == 0)
12515         mng_info->write_png_compression_filter = 2;
12516 
12517       else if (LocaleCompare(value,"2") == 0)
12518         mng_info->write_png_compression_filter = 3;
12519 
12520       else if (LocaleCompare(value,"3") == 0)
12521         mng_info->write_png_compression_filter = 4;
12522 
12523       else if (LocaleCompare(value,"4") == 0)
12524         mng_info->write_png_compression_filter = 5;
12525 
12526       else if (LocaleCompare(value,"5") == 0)
12527         mng_info->write_png_compression_filter = 6;
12528 
12529       else
12530         (void) ThrowMagickException(exception,
12531              GetMagickModule(),CoderWarning,
12532              "ignoring invalid defined png:compression-filter",
12533              "=%s",value);
12534   }
12535 
12536   for (source=0; source<8; source++)
12537   {
12538     value = (const char *) NULL;
12539 
12540     switch(source)
12541     {
12542       case 0:
12543         value=GetImageOption(image_info,"png:exclude-chunks");
12544         break;
12545       case 1:
12546         value=GetImageArtifact(image,"png:exclude-chunks");
12547         break;
12548       case 2:
12549         value=GetImageOption(image_info,"png:exclude-chunk");
12550         break;
12551       case 3:
12552         value=GetImageArtifact(image,"png:exclude-chunk");
12553         break;
12554       case 4:
12555         value=GetImageOption(image_info,"png:include-chunks");
12556         break;
12557       case 5:
12558         value=GetImageArtifact(image,"png:include-chunks");
12559         break;
12560       case 6:
12561         value=GetImageOption(image_info,"png:include-chunk");
12562         break;
12563       case 7:
12564         value=GetImageArtifact(image,"png:include-chunk");
12565         break;
12566     }
12567 
12568     if (value == NULL)
12569        continue;
12570 
12571     if (source < 4)
12572       excluding = MagickTrue;
12573     else
12574       excluding = MagickFalse;
12575 
12576     if (logging != MagickFalse)
12577       {
12578         if (source == 0 || source == 2)
12579            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12580               "  png:exclude-chunk=%s found in image options.\n", value);
12581         else if (source == 1 || source == 3)
12582            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12583               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12584         else if (source == 4 || source == 6)
12585            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12586               "  png:include-chunk=%s found in image options.\n", value);
12587         else /* if (source == 5 || source == 7) */
12588            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12589               "  png:include-chunk=%s found in image artifacts.\n", value);
12590       }
12591 
12592     if (IsOptionMember("all",value) != MagickFalse)
12593       {
12594         mng_info->ping_exclude_bKGD=excluding;
12595         mng_info->ping_exclude_caNv=excluding;
12596         mng_info->ping_exclude_cHRM=excluding;
12597         mng_info->ping_exclude_date=excluding;
12598         mng_info->ping_exclude_EXIF=excluding;
12599         mng_info->ping_exclude_eXIf=excluding;
12600         mng_info->ping_exclude_gAMA=excluding;
12601         mng_info->ping_exclude_iCCP=excluding;
12602         /* mng_info->ping_exclude_iTXt=excluding; */
12603         mng_info->ping_exclude_oFFs=excluding;
12604         mng_info->ping_exclude_pHYs=excluding;
12605         mng_info->ping_exclude_sRGB=excluding;
12606         mng_info->ping_exclude_tEXt=excluding;
12607         mng_info->ping_exclude_tIME=excluding;
12608         mng_info->ping_exclude_tRNS=excluding;
12609         mng_info->ping_exclude_zCCP=excluding;
12610         mng_info->ping_exclude_zTXt=excluding;
12611       }
12612 
12613     if (IsOptionMember("none",value) != MagickFalse)
12614       {
12615         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12616           MagickTrue;
12617         mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12618           MagickTrue;
12619         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12620           MagickTrue;
12621         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12622           MagickTrue;
12623         mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12624           MagickTrue;
12625         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12626           MagickTrue;
12627         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12628           MagickTrue;
12629         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12630           MagickTrue;
12631         /* mng_info->ping_exclude_iTXt=!excluding; */
12632         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12633           MagickTrue;
12634         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12635           MagickTrue;
12636         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12637           MagickTrue;
12638         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12639           MagickTrue;
12640         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12641           MagickTrue;
12642         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12643           MagickTrue;
12644         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12645           MagickTrue;
12646         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12647           MagickTrue;
12648       }
12649 
12650     if (IsOptionMember("bkgd",value) != MagickFalse)
12651       mng_info->ping_exclude_bKGD=excluding;
12652 
12653     if (IsOptionMember("caNv",value) != MagickFalse)
12654       mng_info->ping_exclude_caNv=excluding;
12655 
12656     if (IsOptionMember("chrm",value) != MagickFalse)
12657       mng_info->ping_exclude_cHRM=excluding;
12658 
12659     if (IsOptionMember("date",value) != MagickFalse)
12660       mng_info->ping_exclude_date=excluding;
12661 
12662     if (IsOptionMember("exif",value) != MagickFalse)
12663       {
12664         mng_info->ping_exclude_EXIF=excluding;
12665         mng_info->ping_exclude_eXIf=excluding;
12666       }
12667 
12668     if (IsOptionMember("gama",value) != MagickFalse)
12669       mng_info->ping_exclude_gAMA=excluding;
12670 
12671     if (IsOptionMember("iccp",value) != MagickFalse)
12672       mng_info->ping_exclude_iCCP=excluding;
12673 
12674 #if 0
12675     if (IsOptionMember("itxt",value) != MagickFalse)
12676       mng_info->ping_exclude_iTXt=excluding;
12677 #endif
12678 
12679     if (IsOptionMember("offs",value) != MagickFalse)
12680       mng_info->ping_exclude_oFFs=excluding;
12681 
12682     if (IsOptionMember("phys",value) != MagickFalse)
12683       mng_info->ping_exclude_pHYs=excluding;
12684 
12685     if (IsOptionMember("srgb",value) != MagickFalse)
12686       mng_info->ping_exclude_sRGB=excluding;
12687 
12688     if (IsOptionMember("text",value) != MagickFalse)
12689       mng_info->ping_exclude_tEXt=excluding;
12690 
12691     if (IsOptionMember("time",value) != MagickFalse)
12692       mng_info->ping_exclude_tIME=excluding;
12693 
12694     if (IsOptionMember("trns",value) != MagickFalse)
12695       mng_info->ping_exclude_tRNS=excluding;
12696 
12697     if (IsOptionMember("zccp",value) != MagickFalse)
12698       mng_info->ping_exclude_zCCP=excluding;
12699 
12700     if (IsOptionMember("ztxt",value) != MagickFalse)
12701       mng_info->ping_exclude_zTXt=excluding;
12702   }
12703 
12704   if (logging != MagickFalse)
12705   {
12706     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12707       "  Chunks to be excluded from the output png:");
12708     if (mng_info->ping_exclude_bKGD != MagickFalse)
12709       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12710           "    bKGD");
12711     if (mng_info->ping_exclude_caNv != MagickFalse)
12712       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12713           "    caNv");
12714     if (mng_info->ping_exclude_cHRM != MagickFalse)
12715       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716           "    cHRM");
12717     if (mng_info->ping_exclude_date != MagickFalse)
12718       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12719           "    date");
12720     if (mng_info->ping_exclude_EXIF != MagickFalse)
12721       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12722           "    EXIF");
12723     if (mng_info->ping_exclude_eXIf != MagickFalse)
12724       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12725           "    eXIf");
12726     if (mng_info->ping_exclude_gAMA != MagickFalse)
12727       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12728           "    gAMA");
12729     if (mng_info->ping_exclude_iCCP != MagickFalse)
12730       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12731           "    iCCP");
12732 #if 0
12733     if (mng_info->ping_exclude_iTXt != MagickFalse)
12734       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12735           "    iTXt");
12736 #endif
12737 
12738     if (mng_info->ping_exclude_oFFs != MagickFalse)
12739       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12740           "    oFFs");
12741     if (mng_info->ping_exclude_pHYs != MagickFalse)
12742       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12743           "    pHYs");
12744     if (mng_info->ping_exclude_sRGB != MagickFalse)
12745       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12746           "    sRGB");
12747     if (mng_info->ping_exclude_tEXt != MagickFalse)
12748       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12749           "    tEXt");
12750     if (mng_info->ping_exclude_tIME != MagickFalse)
12751       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12752           "    tIME");
12753     if (mng_info->ping_exclude_tRNS != MagickFalse)
12754       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12755           "    tRNS");
12756     if (mng_info->ping_exclude_zCCP != MagickFalse)
12757       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12758           "    zCCP");
12759     if (mng_info->ping_exclude_zTXt != MagickFalse)
12760       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12761           "    zTXt");
12762   }
12763 
12764   mng_info->need_blob = MagickTrue;
12765 
12766   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12767 
12768   mng_info=MngInfoFreeStruct(mng_info);
12769 
12770   if (logging != MagickFalse)
12771     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12772 
12773   return(status);
12774 }
12775 
12776 #if defined(JNG_SUPPORTED)
12777 
12778 /* Write one JNG image */
WriteOneJNGImage(MngInfo * mng_info,const ImageInfo * image_info,Image * image,ExceptionInfo * exception)12779 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12780    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12781 {
12782   Image
12783     *jpeg_image;
12784 
12785   ImageInfo
12786     *jpeg_image_info;
12787 
12788   MagickBooleanType
12789     logging,
12790     status;
12791 
12792   size_t
12793     length;
12794 
12795   unsigned char
12796     *blob,
12797     chunk[80],
12798     *p;
12799 
12800   unsigned int
12801     jng_alpha_compression_method,
12802     jng_alpha_sample_depth,
12803     jng_color_type,
12804     transparent;
12805 
12806   size_t
12807     jng_alpha_quality,
12808     jng_quality;
12809 
12810   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12811     "  Enter WriteOneJNGImage()");
12812 
12813   if ((image->columns > 65500U) || (image->rows > 65500U))
12814     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
12815 
12816   blob=(unsigned char *) NULL;
12817   jpeg_image=(Image *) NULL;
12818   jpeg_image_info=(ImageInfo *) NULL;
12819   length=0;
12820 
12821   status=MagickTrue;
12822   transparent=image_info->type==GrayscaleAlphaType ||
12823      image_info->type==TrueColorAlphaType ||
12824      image->alpha_trait != UndefinedPixelTrait;
12825 
12826   jng_alpha_sample_depth = 0;
12827 
12828   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12829 
12830   jng_alpha_compression_method=(image->compression==JPEGCompression ||
12831     image_info->compression==JPEGCompression) ? 8 : 0;
12832 
12833   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12834       image_info->quality;
12835 
12836   if (jng_alpha_quality >= 1000)
12837     jng_alpha_quality /= 1000;
12838 
12839   length=0;
12840 
12841   if (transparent != 0)
12842     {
12843       jng_color_type=14;
12844 
12845       /* Create JPEG blob, image, and image_info */
12846       if (logging != MagickFalse)
12847         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12848           "  Creating jpeg_image_info for alpha.");
12849 
12850       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12851 
12852       if (jpeg_image_info == (ImageInfo *) NULL)
12853         {
12854           jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12855           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12856         }
12857 
12858       if (logging != MagickFalse)
12859         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12860           "  Creating jpeg_image.");
12861 
12862       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12863       if (jpeg_image == (Image *) NULL)
12864         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12865       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12866       jpeg_image->alpha_trait=UndefinedPixelTrait;
12867       jpeg_image->quality=jng_alpha_quality;
12868       jpeg_image_info->type=GrayscaleType;
12869       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12870       (void) AcquireUniqueFilename(jpeg_image->filename);
12871       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12872         "%s",jpeg_image->filename);
12873     }
12874   else
12875     {
12876       jng_alpha_compression_method=0;
12877       jng_color_type=10;
12878       jng_alpha_sample_depth=0;
12879     }
12880 
12881   /* To do: check bit depth of PNG alpha channel */
12882 
12883   /* Check if image is grayscale. */
12884   if (image_info->type != TrueColorAlphaType && image_info->type !=
12885     TrueColorType && SetImageGray(image,exception))
12886     jng_color_type-=2;
12887 
12888   if (logging != MagickFalse)
12889     {
12890         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12891           "    JNG Quality           = %d",(int) jng_quality);
12892         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12893           "    JNG Color Type        = %d",jng_color_type);
12894         if (transparent != 0)
12895           {
12896             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12897               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12898             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12899               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12900             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12901               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12902           }
12903     }
12904 
12905   if (transparent != 0)
12906     {
12907       if (jng_alpha_compression_method==0)
12908         {
12909           const char
12910             *value;
12911 
12912           /* Encode alpha as a grayscale PNG blob */
12913           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12914             exception);
12915           if (status == MagickFalse)
12916             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12917 
12918           if (logging != MagickFalse)
12919             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12920               "  Creating PNG blob.");
12921 
12922           (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12923              MagickPathExtent);
12924           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12925           jpeg_image_info->interlace=NoInterlace;
12926 
12927           /* Exclude all ancillary chunks */
12928           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12929 
12930           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12931             &length,exception);
12932 
12933           /* Retrieve sample depth used */
12934           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12935           if (value != (char *) NULL)
12936             jng_alpha_sample_depth= (unsigned int) value[0];
12937         }
12938       else
12939         {
12940           /* Encode alpha as a grayscale JPEG blob */
12941 
12942           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12943             exception);
12944           if (status == MagickFalse)
12945             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12946 
12947           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12948             MagickPathExtent);
12949           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12950           jpeg_image_info->interlace=NoInterlace;
12951           if (logging != MagickFalse)
12952             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12953               "  Creating blob.");
12954           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12955             &length,exception);
12956           if (blob == (unsigned char *) NULL)
12957             {
12958               if (jpeg_image != (Image *)NULL)
12959                 jpeg_image=DestroyImage(jpeg_image);
12960               if (jpeg_image_info != (ImageInfo *)NULL)
12961                 jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12962               return(MagickFalse);
12963             }
12964           jng_alpha_sample_depth=8;
12965 
12966           if (logging != MagickFalse)
12967             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12968               "  Successfully read jpeg_image into a blob, length=%.20g.",
12969               (double) length);
12970 
12971         }
12972       /* Destroy JPEG image and image_info */
12973       jpeg_image=DestroyImage(jpeg_image);
12974       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12975       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12976     }
12977 
12978   /* Write JHDR chunk */
12979   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12980   PNGType(chunk,mng_JHDR);
12981   LogPNGChunk(logging,mng_JHDR,16L);
12982   PNGLong(chunk+4,(png_uint_32) image->columns);
12983   PNGLong(chunk+8,(png_uint_32) image->rows);
12984   chunk[12]=jng_color_type;
12985   chunk[13]=8;  /* sample depth */
12986   chunk[14]=8; /*jng_image_compression_method */
12987   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12988   chunk[16]=jng_alpha_sample_depth;
12989   chunk[17]=jng_alpha_compression_method;
12990   chunk[18]=0; /*jng_alpha_filter_method */
12991   chunk[19]=0; /*jng_alpha_interlace_method */
12992   (void) WriteBlob(image,20,chunk);
12993   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12994   if (logging != MagickFalse)
12995     {
12996       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12997         "    JNG width:%15lu",(unsigned long) image->columns);
12998 
12999       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13000         "    JNG height:%14lu",(unsigned long) image->rows);
13001 
13002       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13003         "    JNG color type:%10d",jng_color_type);
13004 
13005       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13006         "    JNG sample depth:%8d",8);
13007 
13008       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13009         "    JNG compression:%9d",8);
13010 
13011       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13012         "    JNG interlace:%11d",0);
13013 
13014       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13015         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
13016 
13017       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13018         "    JNG alpha compression:%3d",jng_alpha_compression_method);
13019 
13020       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13021         "    JNG alpha filter:%8d",0);
13022 
13023       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13024         "    JNG alpha interlace:%5d",0);
13025     }
13026 
13027   /*
13028      Write leading ancillary chunks
13029   */
13030 
13031   if (transparent != 0)
13032   {
13033     /*
13034       Write JNG bKGD chunk
13035     */
13036 
13037     unsigned char
13038       blue,
13039       green,
13040       red;
13041 
13042     ssize_t
13043       num_bytes;
13044 
13045     if (jng_color_type == 8 || jng_color_type == 12)
13046       num_bytes=6L;
13047     else
13048       num_bytes=10L;
13049     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
13050     PNGType(chunk,mng_bKGD);
13051     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
13052     red=ScaleQuantumToChar(image->background_color.red);
13053     green=ScaleQuantumToChar(image->background_color.green);
13054     blue=ScaleQuantumToChar(image->background_color.blue);
13055     *(chunk+4)=0;
13056     *(chunk+5)=red;
13057     *(chunk+6)=0;
13058     *(chunk+7)=green;
13059     *(chunk+8)=0;
13060     *(chunk+9)=blue;
13061     (void) WriteBlob(image,(size_t) num_bytes,chunk);
13062     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
13063   }
13064 
13065   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
13066     {
13067       /*
13068         Write JNG sRGB chunk
13069       */
13070       (void) WriteBlobMSBULong(image,1L);
13071       PNGType(chunk,mng_sRGB);
13072       LogPNGChunk(logging,mng_sRGB,1L);
13073 
13074       if (image->rendering_intent != UndefinedIntent)
13075         chunk[4]=(unsigned char)
13076           Magick_RenderingIntent_to_PNG_RenderingIntent(
13077           (image->rendering_intent));
13078 
13079       else
13080         chunk[4]=(unsigned char)
13081           Magick_RenderingIntent_to_PNG_RenderingIntent(
13082           (PerceptualIntent));
13083 
13084       (void) WriteBlob(image,5,chunk);
13085       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13086     }
13087   else
13088     {
13089       if (image->gamma != 0.0)
13090         {
13091           /*
13092              Write JNG gAMA chunk
13093           */
13094           (void) WriteBlobMSBULong(image,4L);
13095           PNGType(chunk,mng_gAMA);
13096           LogPNGChunk(logging,mng_gAMA,4L);
13097           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13098           (void) WriteBlob(image,8,chunk);
13099           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13100         }
13101 
13102       if ((mng_info->equal_chrms == MagickFalse) &&
13103           (image->chromaticity.red_primary.x != 0.0))
13104         {
13105           PrimaryInfo
13106             primary;
13107 
13108           /*
13109              Write JNG cHRM chunk
13110           */
13111           (void) WriteBlobMSBULong(image,32L);
13112           PNGType(chunk,mng_cHRM);
13113           LogPNGChunk(logging,mng_cHRM,32L);
13114           primary=image->chromaticity.white_point;
13115           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13116           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13117           primary=image->chromaticity.red_primary;
13118           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13119           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13120           primary=image->chromaticity.green_primary;
13121           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13122           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13123           primary=image->chromaticity.blue_primary;
13124           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13125           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13126           (void) WriteBlob(image,36,chunk);
13127           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13128         }
13129     }
13130 
13131   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
13132     {
13133       /*
13134          Write JNG pHYs chunk
13135       */
13136       (void) WriteBlobMSBULong(image,9L);
13137       PNGType(chunk,mng_pHYs);
13138       LogPNGChunk(logging,mng_pHYs,9L);
13139       if (image->units == PixelsPerInchResolution)
13140         {
13141           PNGLong(chunk+4,(png_uint_32)
13142             (image->resolution.x*100.0/2.54+0.5));
13143 
13144           PNGLong(chunk+8,(png_uint_32)
13145             (image->resolution.y*100.0/2.54+0.5));
13146 
13147           chunk[12]=1;
13148         }
13149 
13150       else
13151         {
13152           if (image->units == PixelsPerCentimeterResolution)
13153             {
13154               PNGLong(chunk+4,(png_uint_32)
13155                 (image->resolution.x*100.0+0.5));
13156 
13157               PNGLong(chunk+8,(png_uint_32)
13158                 (image->resolution.y*100.0+0.5));
13159 
13160               chunk[12]=1;
13161             }
13162 
13163           else
13164             {
13165               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13166               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13167               chunk[12]=0;
13168             }
13169         }
13170       (void) WriteBlob(image,13,chunk);
13171       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13172     }
13173 
13174   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13175     {
13176       /*
13177          Write JNG oFFs chunk
13178       */
13179       (void) WriteBlobMSBULong(image,9L);
13180       PNGType(chunk,mng_oFFs);
13181       LogPNGChunk(logging,mng_oFFs,9L);
13182       PNGsLong(chunk+4,(ssize_t) (image->page.x));
13183       PNGsLong(chunk+8,(ssize_t) (image->page.y));
13184       chunk[12]=0;
13185       (void) WriteBlob(image,13,chunk);
13186       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13187     }
13188 
13189   if (transparent != 0)
13190     {
13191       if (jng_alpha_compression_method==0)
13192         {
13193           ssize_t
13194             i;
13195 
13196           size_t
13197             len;
13198 
13199           /* Write IDAT chunk header */
13200           if (logging != MagickFalse)
13201             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13202               "  Write IDAT chunks from blob, length=%.20g.",(double)
13203               length);
13204 
13205           /* Copy IDAT chunks */
13206           len=0;
13207           p=blob+8;
13208           for (i=8; i<(ssize_t) length; i+=len+12)
13209           {
13210             len=(((unsigned int) *(p    ) & 0xff) << 24) +
13211                 (((unsigned int) *(p + 1) & 0xff) << 16) +
13212                 (((unsigned int) *(p + 2) & 0xff) <<  8) +
13213                 (((unsigned int) *(p + 3) & 0xff)      ) ;
13214             p+=4;
13215 
13216             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13217               {
13218                 /* Found an IDAT chunk. */
13219                 (void) WriteBlobMSBULong(image,len);
13220                 LogPNGChunk(logging,mng_IDAT,len);
13221                 (void) WriteBlob(image,len+4,p);
13222                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13223               }
13224 
13225             else
13226               {
13227                 if (logging != MagickFalse)
13228                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13229                     "    Skipping %c%c%c%c chunk, length=%.20g.",
13230                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
13231               }
13232             p+=(8+len);
13233           }
13234         }
13235       else if (length != 0)
13236         {
13237           /* Write JDAA chunk header */
13238           if (logging != MagickFalse)
13239             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13240               "  Write JDAA chunk, length=%.20g.",(double) length);
13241           (void) WriteBlobMSBULong(image,(size_t) length);
13242           PNGType(chunk,mng_JDAA);
13243           LogPNGChunk(logging,mng_JDAA,length);
13244           /* Write JDAT chunk(s) data */
13245           (void) WriteBlob(image,4,chunk);
13246           (void) WriteBlob(image,length,blob);
13247           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13248              (uInt) length));
13249         }
13250       blob=(unsigned char *) RelinquishMagickMemory(blob);
13251     }
13252 
13253   /* Encode image as a JPEG blob */
13254   if (logging != MagickFalse)
13255     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13256       "  Creating jpeg_image_info.");
13257   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13258   if (jpeg_image_info == (ImageInfo *) NULL)
13259     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13260 
13261   if (logging != MagickFalse)
13262     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13263       "  Creating jpeg_image.");
13264 
13265   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13266   if (jpeg_image == (Image *) NULL)
13267     {
13268       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13269       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13270     }
13271   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13272 
13273   (void) AcquireUniqueFilename(jpeg_image->filename);
13274   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13275     jpeg_image->filename);
13276 
13277   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13278     exception);
13279 
13280   if (logging != MagickFalse)
13281     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13282       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13283       (double) jpeg_image->rows);
13284 
13285   if (status == MagickFalse)
13286     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13287 
13288   if (jng_color_type == 8 || jng_color_type == 12)
13289     jpeg_image_info->type=GrayscaleType;
13290 
13291   jpeg_image_info->quality=jng_quality;
13292   jpeg_image->quality=jng_quality;
13293   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13294   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13295 
13296   if (logging != MagickFalse)
13297     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13298       "  Creating blob.");
13299 
13300   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13301     exception);
13302 
13303   if (logging != MagickFalse)
13304     {
13305       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13306         "  Successfully read jpeg_image into a blob, length=%.20g.",
13307         (double) length);
13308 
13309       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13310         "  Write JDAT chunk, length=%.20g.",(double) length);
13311     }
13312 
13313   /* Write JDAT chunk(s) */
13314   (void) WriteBlobMSBULong(image,(size_t) length);
13315   PNGType(chunk,mng_JDAT);
13316   LogPNGChunk(logging,mng_JDAT,length);
13317   (void) WriteBlob(image,4,chunk);
13318   (void) WriteBlob(image,length,blob);
13319   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13320 
13321   jpeg_image=DestroyImage(jpeg_image);
13322   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13323   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13324   blob=(unsigned char *) RelinquishMagickMemory(blob);
13325 
13326   /* Write IEND chunk */
13327   (void) WriteBlobMSBULong(image,0L);
13328   PNGType(chunk,mng_IEND);
13329   LogPNGChunk(logging,mng_IEND,0);
13330   (void) WriteBlob(image,4,chunk);
13331   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13332 
13333   if (logging != MagickFalse)
13334     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13335       "  exit WriteOneJNGImage()");
13336 
13337   return(status);
13338 }
13339 
13340 /*
13341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13342 %                                                                             %
13343 %                                                                             %
13344 %                                                                             %
13345 %   W r i t e J N G I m a g e                                                 %
13346 %                                                                             %
13347 %                                                                             %
13348 %                                                                             %
13349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13350 %
13351 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13352 %
13353 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
13354 %
13355 %  The format of the WriteJNGImage method is:
13356 %
13357 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13358 %        Image *image,ExceptionInfo *exception)
13359 %
13360 %  A description of each parameter follows:
13361 %
13362 %    o image_info: the image info.
13363 %
13364 %    o image:  The image.
13365 %
13366 %    o exception: return any errors or warnings in this structure.
13367 %
13368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13369 */
WriteJNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)13370 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13371   Image *image, ExceptionInfo *exception)
13372 {
13373   MagickBooleanType
13374     logging,
13375     status;
13376 
13377   MngInfo
13378     *mng_info;
13379 
13380   /*
13381     Open image file.
13382   */
13383   assert(image_info != (const ImageInfo *) NULL);
13384   assert(image_info->signature == MagickCoreSignature);
13385   assert(image != (Image *) NULL);
13386   assert(image->signature == MagickCoreSignature);
13387   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13388   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13389   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13390   if (status == MagickFalse)
13391     return(status);
13392   if ((image->columns > 65535UL) || (image->rows > 65535UL))
13393     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13394 
13395   /*
13396     Allocate a MngInfo structure.
13397   */
13398   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13399   if (mng_info == (MngInfo *) NULL)
13400     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13401   /*
13402     Initialize members of the MngInfo structure.
13403   */
13404   (void) memset(mng_info,0,sizeof(MngInfo));
13405   mng_info->image=image;
13406 
13407   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13408 
13409   status=WriteOneJNGImage(mng_info,image_info,image,exception);
13410   mng_info=MngInfoFreeStruct(mng_info);
13411   (void) CloseBlob(image);
13412 
13413   (void) CatchImageException(image);
13414   if (logging != MagickFalse)
13415     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13416   return(status);
13417 }
13418 #endif
13419 
WriteMNGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)13420 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13421   Image *image, ExceptionInfo *exception)
13422 {
13423   Image
13424     *next_image;
13425 
13426   MagickBooleanType
13427     status;
13428 
13429   volatile MagickBooleanType
13430     logging;
13431 
13432   MngInfo
13433     *mng_info;
13434 
13435   int
13436     image_count,
13437     need_iterations,
13438     need_matte;
13439 
13440   volatile int
13441 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13442     defined(PNG_MNG_FEATURES_SUPPORTED)
13443     need_local_plte,
13444 #endif
13445     all_images_are_gray,
13446     need_defi,
13447     use_global_plte;
13448 
13449   ssize_t
13450     i;
13451 
13452   unsigned char
13453     chunk[800];
13454 
13455   volatile unsigned int
13456     write_jng,
13457     write_mng;
13458 
13459   volatile size_t
13460     scene;
13461 
13462   size_t
13463     final_delay=0,
13464     imageListLength,
13465     initial_delay;
13466 
13467 #if (PNG_LIBPNG_VER < 10200)
13468     if (image_info->verbose)
13469       printf("Your PNG library (libpng-%s) is rather old.\n",
13470          PNG_LIBPNG_VER_STRING);
13471 #endif
13472 
13473   /*
13474     Open image file.
13475   */
13476   assert(image_info != (const ImageInfo *) NULL);
13477   assert(image_info->signature == MagickCoreSignature);
13478   assert(image != (Image *) NULL);
13479   assert(image->signature == MagickCoreSignature);
13480   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13481   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13482   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13483   if (status == MagickFalse)
13484     return(status);
13485 
13486   /*
13487     Allocate a MngInfo structure.
13488   */
13489   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13490   if (mng_info == (MngInfo *) NULL)
13491     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13492   /*
13493     Initialize members of the MngInfo structure.
13494   */
13495   (void) memset(mng_info,0,sizeof(MngInfo));
13496   mng_info->image=image;
13497   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13498 
13499   /*
13500    * See if user has requested a specific PNG subformat to be used
13501    * for all of the PNGs in the MNG being written, e.g.,
13502    *
13503    *    convert *.png png8:animation.mng
13504    *
13505    * To do: check -define png:bit_depth and png:color_type as well,
13506    * or perhaps use mng:bit_depth and mng:color_type instead for
13507    * global settings.
13508    */
13509 
13510   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13511   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13512   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13513 
13514   write_jng=MagickFalse;
13515   if (image_info->compression == JPEGCompression)
13516     write_jng=MagickTrue;
13517 
13518   mng_info->adjoin=image_info->adjoin &&
13519     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13520 
13521   if (logging != MagickFalse)
13522     {
13523       /* Log some info about the input */
13524       Image
13525         *p;
13526 
13527       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13528         "  Checking input image(s)\n"
13529         "    Image_info depth: %.20g,    Type: %d",
13530         (double) image_info->depth, image_info->type);
13531 
13532       scene=0;
13533       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13534       {
13535 
13536         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13537            "    Scene: %.20g\n,   Image depth: %.20g",
13538            (double) scene++, (double) p->depth);
13539 
13540         if (p->alpha_trait != UndefinedPixelTrait)
13541           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13542             "      Matte: True");
13543 
13544         else
13545           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13546             "      Matte: False");
13547 
13548         if (p->storage_class == PseudoClass)
13549           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13550             "      Storage class: PseudoClass");
13551 
13552         else
13553           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13554             "      Storage class: DirectClass");
13555 
13556         if (p->colors)
13557           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13558             "      Number of colors: %.20g",(double) p->colors);
13559 
13560         else
13561           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13562             "      Number of colors: unspecified");
13563 
13564         if (mng_info->adjoin == MagickFalse)
13565           break;
13566       }
13567     }
13568 
13569   use_global_plte=MagickFalse;
13570   all_images_are_gray=MagickFalse;
13571 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13572   need_local_plte=MagickTrue;
13573 #endif
13574   need_defi=MagickFalse;
13575   need_matte=MagickFalse;
13576   mng_info->framing_mode=1;
13577   mng_info->old_framing_mode=1;
13578 
13579   if (write_mng)
13580       if (image_info->page != (char *) NULL)
13581         {
13582           /*
13583             Determine image bounding box.
13584           */
13585           SetGeometry(image,&mng_info->page);
13586           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13587             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13588         }
13589   if (write_mng)
13590     {
13591       unsigned int
13592         need_geom;
13593 
13594       unsigned short
13595         red,
13596         green,
13597         blue;
13598 
13599       const char *
13600         option;
13601 
13602       mng_info->page=image->page;
13603       need_geom=MagickTrue;
13604       if (mng_info->page.width || mng_info->page.height)
13605          need_geom=MagickFalse;
13606       /*
13607         Check all the scenes.
13608       */
13609       initial_delay=image->delay;
13610       need_iterations=MagickFalse;
13611       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13612       mng_info->equal_physs=MagickTrue,
13613       mng_info->equal_gammas=MagickTrue;
13614       mng_info->equal_srgbs=MagickTrue;
13615       mng_info->equal_backgrounds=MagickTrue;
13616       image_count=0;
13617 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13618     defined(PNG_MNG_FEATURES_SUPPORTED)
13619       all_images_are_gray=MagickTrue;
13620       mng_info->equal_palettes=MagickFalse;
13621       need_local_plte=MagickFalse;
13622 #endif
13623       for (next_image=image; next_image != (Image *) NULL; )
13624       {
13625         if (need_geom)
13626           {
13627             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13628               mng_info->page.width=next_image->columns+next_image->page.x;
13629 
13630             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13631               mng_info->page.height=next_image->rows+next_image->page.y;
13632           }
13633 
13634         if (next_image->page.x || next_image->page.y)
13635           need_defi=MagickTrue;
13636 
13637         if (next_image->alpha_trait != UndefinedPixelTrait)
13638           need_matte=MagickTrue;
13639 
13640         if ((int) next_image->dispose >= BackgroundDispose)
13641           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13642                next_image->page.x || next_image->page.y ||
13643               ((next_image->columns < mng_info->page.width) &&
13644                (next_image->rows < mng_info->page.height)))
13645             mng_info->need_fram=MagickTrue;
13646 
13647         if (next_image->iterations)
13648           need_iterations=MagickTrue;
13649 
13650         final_delay=next_image->delay;
13651 
13652         if (final_delay != initial_delay || final_delay > 1UL*
13653            next_image->ticks_per_second)
13654           mng_info->need_fram=1;
13655 
13656 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13657     defined(PNG_MNG_FEATURES_SUPPORTED)
13658         /*
13659           check for global palette possibility.
13660         */
13661         if (image->alpha_trait != UndefinedPixelTrait)
13662            need_local_plte=MagickTrue;
13663 
13664         if (need_local_plte == 0)
13665           {
13666             if (SetImageGray(image,exception) == MagickFalse)
13667               all_images_are_gray=MagickFalse;
13668             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13669             if (use_global_plte == 0)
13670               use_global_plte=mng_info->equal_palettes;
13671             need_local_plte=!mng_info->equal_palettes;
13672           }
13673 #endif
13674         if (GetNextImageInList(next_image) != (Image *) NULL)
13675           {
13676             if (next_image->background_color.red !=
13677                 next_image->next->background_color.red ||
13678                 next_image->background_color.green !=
13679                 next_image->next->background_color.green ||
13680                 next_image->background_color.blue !=
13681                 next_image->next->background_color.blue)
13682               mng_info->equal_backgrounds=MagickFalse;
13683 
13684             if (next_image->gamma != next_image->next->gamma)
13685               mng_info->equal_gammas=MagickFalse;
13686 
13687             if (next_image->rendering_intent !=
13688                 next_image->next->rendering_intent)
13689               mng_info->equal_srgbs=MagickFalse;
13690 
13691             if ((next_image->units != next_image->next->units) ||
13692                 (next_image->resolution.x != next_image->next->resolution.x) ||
13693                 (next_image->resolution.y != next_image->next->resolution.y))
13694               mng_info->equal_physs=MagickFalse;
13695 
13696             if (mng_info->equal_chrms)
13697               {
13698                 if (next_image->chromaticity.red_primary.x !=
13699                     next_image->next->chromaticity.red_primary.x ||
13700                     next_image->chromaticity.red_primary.y !=
13701                     next_image->next->chromaticity.red_primary.y ||
13702                     next_image->chromaticity.green_primary.x !=
13703                     next_image->next->chromaticity.green_primary.x ||
13704                     next_image->chromaticity.green_primary.y !=
13705                     next_image->next->chromaticity.green_primary.y ||
13706                     next_image->chromaticity.blue_primary.x !=
13707                     next_image->next->chromaticity.blue_primary.x ||
13708                     next_image->chromaticity.blue_primary.y !=
13709                     next_image->next->chromaticity.blue_primary.y ||
13710                     next_image->chromaticity.white_point.x !=
13711                     next_image->next->chromaticity.white_point.x ||
13712                     next_image->chromaticity.white_point.y !=
13713                     next_image->next->chromaticity.white_point.y)
13714                   mng_info->equal_chrms=MagickFalse;
13715               }
13716           }
13717         image_count++;
13718         next_image=GetNextImageInList(next_image);
13719       }
13720       if (image_count < 2)
13721         {
13722           mng_info->equal_backgrounds=MagickFalse;
13723           mng_info->equal_chrms=MagickFalse;
13724           mng_info->equal_gammas=MagickFalse;
13725           mng_info->equal_srgbs=MagickFalse;
13726           mng_info->equal_physs=MagickFalse;
13727           use_global_plte=MagickFalse;
13728 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13729           need_local_plte=MagickTrue;
13730 #endif
13731           need_iterations=MagickFalse;
13732         }
13733 
13734      if (mng_info->need_fram == MagickFalse)
13735        {
13736          /*
13737            Only certain framing rates 100/n are exactly representable without
13738            the FRAM chunk but we'll allow some slop in VLC files
13739          */
13740          if (final_delay == 0)
13741            {
13742              if (need_iterations != MagickFalse)
13743                {
13744                  /*
13745                    It's probably a GIF with loop; don't run it *too* fast.
13746                  */
13747                  if (mng_info->adjoin)
13748                    {
13749                      final_delay=10;
13750                      (void) ThrowMagickException(exception,GetMagickModule(),
13751                        CoderWarning,
13752                        "input has zero delay between all frames; assuming",
13753                        " 10 cs `%s'","");
13754                    }
13755                }
13756              else
13757                mng_info->ticks_per_second=0;
13758            }
13759          if (final_delay != 0)
13760            mng_info->ticks_per_second=(png_uint_32)
13761               (image->ticks_per_second/final_delay);
13762          if (final_delay > 50)
13763            mng_info->ticks_per_second=2;
13764 
13765          if (final_delay > 75)
13766            mng_info->ticks_per_second=1;
13767 
13768          if (final_delay > 125)
13769            mng_info->need_fram=MagickTrue;
13770 
13771          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13772             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13773             (final_delay != 25) && (final_delay != 50) &&
13774             (final_delay != (size_t) image->ticks_per_second))
13775            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13776        }
13777 
13778      if (mng_info->need_fram != MagickFalse)
13779         mng_info->ticks_per_second=image->ticks_per_second;
13780      /*
13781         If pseudocolor, we should also check to see if all the
13782         palettes are identical and write a global PLTE if they are.
13783         ../glennrp Feb 99.
13784      */
13785      /*
13786         Write the MNG version 1.0 signature and MHDR chunk.
13787      */
13788      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13789      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13790      PNGType(chunk,mng_MHDR);
13791      LogPNGChunk(logging,mng_MHDR,28L);
13792      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13793      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13794      PNGLong(chunk+12,mng_info->ticks_per_second);
13795      PNGLong(chunk+16,0L);  /* layer count=unknown */
13796      PNGLong(chunk+20,0L);  /* frame count=unknown */
13797      PNGLong(chunk+24,0L);  /* play time=unknown   */
13798      if (write_jng)
13799        {
13800          if (need_matte)
13801            {
13802              if (need_defi || mng_info->need_fram || use_global_plte)
13803                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13804 
13805              else
13806                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13807            }
13808 
13809          else
13810            {
13811              if (need_defi || mng_info->need_fram || use_global_plte)
13812                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13813 
13814              else
13815                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13816            }
13817        }
13818 
13819      else
13820        {
13821          if (need_matte)
13822            {
13823              if (need_defi || mng_info->need_fram || use_global_plte)
13824                PNGLong(chunk+28,11L);    /* simplicity=LC */
13825 
13826              else
13827                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13828            }
13829 
13830          else
13831            {
13832              if (need_defi || mng_info->need_fram || use_global_plte)
13833                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13834 
13835              else
13836                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13837            }
13838        }
13839      (void) WriteBlob(image,32,chunk);
13840      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13841      option=GetImageOption(image_info,"mng:need-cacheoff");
13842      if (option != (const char *) NULL)
13843        {
13844          size_t
13845            length;
13846          /*
13847            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13848          */
13849          PNGType(chunk,mng_nEED);
13850          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13851          (void) WriteBlobMSBULong(image,(size_t) length);
13852          LogPNGChunk(logging,mng_nEED,(size_t) length);
13853          length+=4;
13854          (void) WriteBlob(image,length,chunk);
13855          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13856        }
13857      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13858          (GetNextImageInList(image) != (Image *) NULL) &&
13859          (image->iterations != 1))
13860        {
13861          /*
13862            Write MNG TERM chunk
13863          */
13864          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13865          PNGType(chunk,mng_TERM);
13866          LogPNGChunk(logging,mng_TERM,10L);
13867          chunk[4]=3;  /* repeat animation */
13868          chunk[5]=0;  /* show last frame when done */
13869          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13870             final_delay/MagickMax(image->ticks_per_second,1)));
13871 
13872          if (image->iterations == 0)
13873            PNGLong(chunk+10,PNG_UINT_31_MAX);
13874 
13875          else
13876            PNGLong(chunk+10,(png_uint_32) image->iterations);
13877 
13878          if (logging != MagickFalse)
13879            {
13880              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13881                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13882               final_delay/MagickMax(image->ticks_per_second,1)));
13883 
13884              if (image->iterations == 0)
13885                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13886                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13887 
13888              else
13889                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13890                  "     Image iterations: %.20g",(double) image->iterations);
13891            }
13892          (void) WriteBlob(image,14,chunk);
13893          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13894        }
13895      /*
13896        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13897      */
13898      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13899           mng_info->equal_srgbs)
13900        {
13901          /*
13902            Write MNG sRGB chunk
13903          */
13904          (void) WriteBlobMSBULong(image,1L);
13905          PNGType(chunk,mng_sRGB);
13906          LogPNGChunk(logging,mng_sRGB,1L);
13907 
13908          if (image->rendering_intent != UndefinedIntent)
13909            chunk[4]=(unsigned char)
13910              Magick_RenderingIntent_to_PNG_RenderingIntent(
13911              (image->rendering_intent));
13912 
13913          else
13914            chunk[4]=(unsigned char)
13915              Magick_RenderingIntent_to_PNG_RenderingIntent(
13916                (PerceptualIntent));
13917 
13918          (void) WriteBlob(image,5,chunk);
13919          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13920          mng_info->have_write_global_srgb=MagickTrue;
13921        }
13922 
13923      else
13924        {
13925          if (image->gamma && mng_info->equal_gammas)
13926            {
13927              /*
13928                 Write MNG gAMA chunk
13929              */
13930              (void) WriteBlobMSBULong(image,4L);
13931              PNGType(chunk,mng_gAMA);
13932              LogPNGChunk(logging,mng_gAMA,4L);
13933              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13934              (void) WriteBlob(image,8,chunk);
13935              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13936              mng_info->have_write_global_gama=MagickTrue;
13937            }
13938          if (mng_info->equal_chrms)
13939            {
13940              PrimaryInfo
13941                primary;
13942 
13943              /*
13944                 Write MNG cHRM chunk
13945              */
13946              (void) WriteBlobMSBULong(image,32L);
13947              PNGType(chunk,mng_cHRM);
13948              LogPNGChunk(logging,mng_cHRM,32L);
13949              primary=image->chromaticity.white_point;
13950              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13951              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13952              primary=image->chromaticity.red_primary;
13953              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13954              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13955              primary=image->chromaticity.green_primary;
13956              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13957              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13958              primary=image->chromaticity.blue_primary;
13959              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13960              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13961              (void) WriteBlob(image,36,chunk);
13962              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13963              mng_info->have_write_global_chrm=MagickTrue;
13964            }
13965        }
13966      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13967        {
13968          /*
13969             Write MNG pHYs chunk
13970          */
13971          (void) WriteBlobMSBULong(image,9L);
13972          PNGType(chunk,mng_pHYs);
13973          LogPNGChunk(logging,mng_pHYs,9L);
13974 
13975          if (image->units == PixelsPerInchResolution)
13976            {
13977              PNGLong(chunk+4,(png_uint_32)
13978                (image->resolution.x*100.0/2.54+0.5));
13979 
13980              PNGLong(chunk+8,(png_uint_32)
13981                (image->resolution.y*100.0/2.54+0.5));
13982 
13983              chunk[12]=1;
13984            }
13985 
13986          else
13987            {
13988              if (image->units == PixelsPerCentimeterResolution)
13989                {
13990                  PNGLong(chunk+4,(png_uint_32)
13991                    (image->resolution.x*100.0+0.5));
13992 
13993                  PNGLong(chunk+8,(png_uint_32)
13994                    (image->resolution.y*100.0+0.5));
13995 
13996                  chunk[12]=1;
13997                }
13998 
13999              else
14000                {
14001                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
14002                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
14003                  chunk[12]=0;
14004                }
14005            }
14006          (void) WriteBlob(image,13,chunk);
14007          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
14008        }
14009      /*
14010        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
14011        or does not cover the entire frame.
14012      */
14013      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
14014          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
14015          (image->page.width+image->page.x < mng_info->page.width))
14016          || (image->page.height && (image->page.height+image->page.y
14017          < mng_info->page.height))))
14018        {
14019          (void) WriteBlobMSBULong(image,6L);
14020          PNGType(chunk,mng_BACK);
14021          LogPNGChunk(logging,mng_BACK,6L);
14022          red=ScaleQuantumToShort(image->background_color.red);
14023          green=ScaleQuantumToShort(image->background_color.green);
14024          blue=ScaleQuantumToShort(image->background_color.blue);
14025          PNGShort(chunk+4,red);
14026          PNGShort(chunk+6,green);
14027          PNGShort(chunk+8,blue);
14028          (void) WriteBlob(image,10,chunk);
14029          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14030          if (mng_info->equal_backgrounds)
14031            {
14032              (void) WriteBlobMSBULong(image,6L);
14033              PNGType(chunk,mng_bKGD);
14034              LogPNGChunk(logging,mng_bKGD,6L);
14035              (void) WriteBlob(image,10,chunk);
14036              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
14037            }
14038        }
14039 
14040 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
14041      if ((need_local_plte == MagickFalse) &&
14042          (image->storage_class == PseudoClass) &&
14043          (all_images_are_gray == MagickFalse))
14044        {
14045          size_t
14046            data_length;
14047 
14048          /*
14049            Write MNG PLTE chunk
14050          */
14051          data_length=3*image->colors;
14052          (void) WriteBlobMSBULong(image,data_length);
14053          PNGType(chunk,mng_PLTE);
14054          LogPNGChunk(logging,mng_PLTE,data_length);
14055 
14056          for (i=0; i < (ssize_t) image->colors; i++)
14057          {
14058            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
14059              image->colormap[i].red) & 0xff);
14060            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
14061              image->colormap[i].green) & 0xff);
14062            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
14063              image->colormap[i].blue) & 0xff);
14064          }
14065 
14066          (void) WriteBlob(image,data_length+4,chunk);
14067          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
14068          mng_info->have_write_global_plte=MagickTrue;
14069        }
14070 #endif
14071     }
14072   scene=0;
14073   mng_info->delay=0;
14074 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14075     defined(PNG_MNG_FEATURES_SUPPORTED)
14076   mng_info->equal_palettes=MagickFalse;
14077 #endif
14078   imageListLength=GetImageListLength(image);
14079   do
14080   {
14081     if (mng_info->adjoin)
14082     {
14083 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
14084     defined(PNG_MNG_FEATURES_SUPPORTED)
14085     /*
14086       If we aren't using a global palette for the entire MNG, check to
14087       see if we can use one for two or more consecutive images.
14088     */
14089     if (need_local_plte && use_global_plte && !all_images_are_gray)
14090       {
14091         if (mng_info->IsPalette)
14092           {
14093             /*
14094               When equal_palettes is true, this image has the same palette
14095               as the previous PseudoClass image
14096             */
14097             mng_info->have_write_global_plte=mng_info->equal_palettes;
14098             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
14099             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
14100               {
14101                 /*
14102                   Write MNG PLTE chunk
14103                 */
14104                 size_t
14105                   data_length;
14106 
14107                 data_length=3*image->colors;
14108                 (void) WriteBlobMSBULong(image,data_length);
14109                 PNGType(chunk,mng_PLTE);
14110                 LogPNGChunk(logging,mng_PLTE,data_length);
14111 
14112                 for (i=0; i < (ssize_t) image->colors; i++)
14113                 {
14114                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
14115                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
14116                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
14117                 }
14118 
14119                 (void) WriteBlob(image,data_length+4,chunk);
14120                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
14121                    (uInt) (data_length+4)));
14122                 mng_info->have_write_global_plte=MagickTrue;
14123               }
14124           }
14125         else
14126           mng_info->have_write_global_plte=MagickFalse;
14127       }
14128 #endif
14129     if (need_defi)
14130       {
14131         ssize_t
14132           previous_x,
14133           previous_y;
14134 
14135         if (scene != 0)
14136           {
14137             previous_x=mng_info->page.x;
14138             previous_y=mng_info->page.y;
14139           }
14140         else
14141           {
14142             previous_x=0;
14143             previous_y=0;
14144           }
14145         mng_info->page=image->page;
14146         if ((mng_info->page.x !=  previous_x) ||
14147             (mng_info->page.y != previous_y))
14148           {
14149              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
14150              PNGType(chunk,mng_DEFI);
14151              LogPNGChunk(logging,mng_DEFI,12L);
14152              chunk[4]=0; /* object 0 MSB */
14153              chunk[5]=0; /* object 0 LSB */
14154              chunk[6]=0; /* visible  */
14155              chunk[7]=0; /* abstract */
14156              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14157              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14158              (void) WriteBlob(image,16,chunk);
14159              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14160           }
14161       }
14162     }
14163 
14164    mng_info->write_mng=write_mng;
14165 
14166    if ((int) image->dispose >= 3)
14167      mng_info->framing_mode=3;
14168 
14169    if (mng_info->need_fram && mng_info->adjoin &&
14170        ((image->delay != mng_info->delay) ||
14171         (mng_info->framing_mode != mng_info->old_framing_mode)))
14172      {
14173        if (image->delay == mng_info->delay)
14174          {
14175            /*
14176              Write a MNG FRAM chunk with the new framing mode.
14177            */
14178            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
14179            PNGType(chunk,mng_FRAM);
14180            LogPNGChunk(logging,mng_FRAM,1L);
14181            chunk[4]=(unsigned char) mng_info->framing_mode;
14182            (void) WriteBlob(image,5,chunk);
14183            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14184          }
14185        else
14186          {
14187            /*
14188              Write a MNG FRAM chunk with the delay.
14189            */
14190            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
14191            PNGType(chunk,mng_FRAM);
14192            LogPNGChunk(logging,mng_FRAM,10L);
14193            chunk[4]=(unsigned char) mng_info->framing_mode;
14194            chunk[5]=0;  /* frame name separator (no name) */
14195            chunk[6]=2;  /* flag for changing default delay */
14196            chunk[7]=0;  /* flag for changing frame timeout */
14197            chunk[8]=0;  /* flag for changing frame clipping */
14198            chunk[9]=0;  /* flag for changing frame sync_id */
14199            PNGLong(chunk+10,(png_uint_32)
14200              ((mng_info->ticks_per_second*
14201              image->delay)/MagickMax(image->ticks_per_second,1)));
14202            (void) WriteBlob(image,14,chunk);
14203            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14204            mng_info->delay=(png_uint_32) image->delay;
14205          }
14206        mng_info->old_framing_mode=mng_info->framing_mode;
14207      }
14208 
14209 #if defined(JNG_SUPPORTED)
14210    if (image_info->compression == JPEGCompression)
14211      {
14212        ImageInfo
14213          *write_info;
14214 
14215        if (logging != MagickFalse)
14216          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14217            "  Writing JNG object.");
14218        /* To do: specify the desired alpha compression method. */
14219        write_info=CloneImageInfo(image_info);
14220        write_info->compression=UndefinedCompression;
14221        status=WriteOneJNGImage(mng_info,write_info,image,exception);
14222        write_info=DestroyImageInfo(write_info);
14223      }
14224    else
14225 #endif
14226      {
14227        if (logging != MagickFalse)
14228          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14229            "  Writing PNG object.");
14230 
14231        mng_info->need_blob = MagickFalse;
14232        mng_info->ping_preserve_colormap = MagickFalse;
14233 
14234        /* We don't want any ancillary chunks written */
14235        mng_info->ping_exclude_bKGD=MagickTrue;
14236        mng_info->ping_exclude_caNv=MagickTrue;
14237        mng_info->ping_exclude_cHRM=MagickTrue;
14238        mng_info->ping_exclude_date=MagickTrue;
14239        mng_info->ping_exclude_EXIF=MagickTrue;
14240        mng_info->ping_exclude_gAMA=MagickTrue;
14241        mng_info->ping_exclude_iCCP=MagickTrue;
14242        /* mng_info->ping_exclude_iTXt=MagickTrue; */
14243        mng_info->ping_exclude_oFFs=MagickTrue;
14244        mng_info->ping_exclude_pHYs=MagickTrue;
14245        mng_info->ping_exclude_sRGB=MagickTrue;
14246        mng_info->ping_exclude_tEXt=MagickTrue;
14247        mng_info->ping_exclude_tRNS=MagickTrue;
14248        mng_info->ping_exclude_zCCP=MagickTrue;
14249        mng_info->ping_exclude_zTXt=MagickTrue;
14250 
14251        status=WriteOnePNGImage(mng_info,image_info,image,exception);
14252      }
14253 
14254     if (status == MagickFalse)
14255       {
14256         mng_info=MngInfoFreeStruct(mng_info);
14257         (void) CloseBlob(image);
14258         return(MagickFalse);
14259       }
14260     (void) CatchImageException(image);
14261     if (GetNextImageInList(image) == (Image *) NULL)
14262       break;
14263     image=SyncNextImageInList(image);
14264     status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
14265 
14266     if (status == MagickFalse)
14267       break;
14268 
14269   } while (mng_info->adjoin);
14270 
14271   if (write_mng)
14272     {
14273       while (GetPreviousImageInList(image) != (Image *) NULL)
14274         image=GetPreviousImageInList(image);
14275       /*
14276         Write the MEND chunk.
14277       */
14278       (void) WriteBlobMSBULong(image,0x00000000L);
14279       PNGType(chunk,mng_MEND);
14280       LogPNGChunk(logging,mng_MEND,0L);
14281       (void) WriteBlob(image,4,chunk);
14282       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14283     }
14284   /*
14285     Relinquish resources.
14286   */
14287   (void) CloseBlob(image);
14288   mng_info=MngInfoFreeStruct(mng_info);
14289 
14290   if (logging != MagickFalse)
14291     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14292 
14293   return(MagickTrue);
14294 }
14295 #else /* PNG_LIBPNG_VER > 10011 */
14296 
WritePNGImage(const ImageInfo * image_info,Image * image)14297 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14298   Image *image)
14299 {
14300   (void) image;
14301   printf("Your PNG library is too old: You have libpng-%s\n",
14302      PNG_LIBPNG_VER_STRING);
14303 
14304   ThrowBinaryException(CoderError,"PNG library is too old",
14305      image_info->filename);
14306 }
14307 
WriteMNGImage(const ImageInfo * image_info,Image * image)14308 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14309   Image *image)
14310 {
14311   return(WritePNGImage(image_info,image));
14312 }
14313 #endif /* PNG_LIBPNG_VER > 10011 */
14314 #endif
14315