]> granicus.if.org Git - imagemagick/blob - coders/png.c
Restore dirk's recent change to ReadOneJNGImage
[imagemagick] / coders / png.c
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-2017 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://www.imagemagick.org/script/license.php                           %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 #define IM
41
42 \f
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/colorspace.h"
57 #include "MagickCore/colorspace-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/enhance.h"
60 #include "MagickCore/exception.h"
61 #include "MagickCore/exception-private.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/histogram.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/layer.h"
67 #include "MagickCore/list.h"
68 #include "MagickCore/log.h"
69 #include "MagickCore/MagickCore.h"
70 #include "MagickCore/memory_.h"
71 #include "MagickCore/module.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/option.h"
75 #include "MagickCore/pixel.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/profile.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/semaphore.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/static.h"
84 #include "MagickCore/statistic.h"
85 #include "MagickCore/string_.h"
86 #include "MagickCore/string-private.h"
87 #include "MagickCore/transform.h"
88 #include "MagickCore/utility.h"
89 #if defined(MAGICKCORE_PNG_DELEGATE)
90
91 /* Suppress libpng pedantic warnings that were added in
92  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
93  * migration to libpng-1.5, remove these defines and then
94  * fix any code that generates warnings.
95  */
96 /* #define PNG_DEPRECATED   Use of this function is deprecated */
97 /* #define PNG_USE_RESULT   The result of this function must be checked */
98 /* #define PNG_NORETURN     This function does not return */
99 /* #define PNG_ALLOCATED    The result of the function is new memory */
100 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
101
102 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
103 #define PNG_PTR_NORETURN
104
105 #include "png.h"
106 #include "zlib.h"
107 \f
108 /* ImageMagick differences */
109 #define first_scene scene
110
111 #if PNG_LIBPNG_VER > 10011
112 /*
113   Optional declarations. Define or undefine them as you like.
114 */
115 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
116
117 /*
118   Features under construction.  Define these to work on them.
119 */
120 #undef MNG_OBJECT_BUFFERS
121 #undef MNG_BASI_SUPPORTED
122 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
123 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
124 #if defined(MAGICKCORE_JPEG_DELEGATE)
125 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
126 #endif
127 #if !defined(RGBColorMatchExact)
128 #define IsPNGColorEqual(color,target) \
129        (((color).red == (target).red) && \
130         ((color).green == (target).green) && \
131         ((color).blue == (target).blue))
132 #endif
133
134 /* Table of recognized sRGB ICC profiles */
135 struct sRGB_info_struct
136 {
137     png_uint_32 len;
138     png_uint_32 crc;
139     png_byte intent;
140 };
141
142 const struct sRGB_info_struct sRGB_info[] =
143 {
144     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
145     { 3048, 0x3b8772b9UL, 0},
146
147     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
148     { 3052, 0x427ebb21UL, 1},
149
150     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
151     {60988, 0x306fd8aeUL, 0},
152
153     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
154      {60960, 0xbbef7812UL, 0},
155
156     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
157      { 3024, 0x5d5129ceUL, 1},
158
159      /* HP-Microsoft sRGB v2 perceptual */
160      { 3144, 0x182ea552UL, 0},
161
162      /* HP-Microsoft sRGB v2 media-relative */
163      { 3144, 0xf29e526dUL, 1},
164
165      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
166      {  524, 0xd4938c39UL, 0},
167
168      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
169      { 3212, 0x034af5a1UL, 0},
170
171      /* Not recognized */
172      {    0, 0x00000000UL, 0},
173 };
174
175 /* Macros for left-bit-replication to ensure that pixels
176  * and PixelInfos all have the same image->depth, and for use
177  * in PNG8 quantization.
178  */
179
180 /* LBR01: Replicate top bit */
181
182 #define LBR01PacketRed(pixelpacket) \
183      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
184         0 : QuantumRange);
185
186 #define LBR01PacketGreen(pixelpacket) \
187      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
188         0 : QuantumRange);
189
190 #define LBR01PacketBlue(pixelpacket) \
191      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
192         0 : QuantumRange);
193
194 #define LBR01PacketAlpha(pixelpacket) \
195      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
196         0 : QuantumRange);
197
198 #define LBR01PacketRGB(pixelpacket) \
199         { \
200         LBR01PacketRed((pixelpacket)); \
201         LBR01PacketGreen((pixelpacket)); \
202         LBR01PacketBlue((pixelpacket)); \
203         }
204
205 #define LBR01PacketRGBO(pixelpacket) \
206         { \
207         LBR01PacketRGB((pixelpacket)); \
208         LBR01PacketAlpha((pixelpacket)); \
209         }
210
211 #define LBR01PixelRed(pixel) \
212         (SetPixelRed(image, \
213         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
214         0 : QuantumRange,(pixel)));
215
216 #define LBR01PixelGreen(pixel) \
217         (SetPixelGreen(image, \
218         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
219         0 : QuantumRange,(pixel)));
220
221 #define LBR01PixelBlue(pixel) \
222         (SetPixelBlue(image, \
223         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
224         0 : QuantumRange,(pixel)));
225
226 #define LBR01PixelAlpha(pixel) \
227         (SetPixelAlpha(image, \
228         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
229         0 : QuantumRange,(pixel)));
230
231 #define LBR01PixelRGB(pixel) \
232         { \
233         LBR01PixelRed((pixel)); \
234         LBR01PixelGreen((pixel)); \
235         LBR01PixelBlue((pixel)); \
236         }
237
238 #define LBR01PixelRGBA(pixel) \
239         { \
240         LBR01PixelRGB((pixel)); \
241         LBR01PixelAlpha((pixel)); \
242         }
243
244 /* LBR02: Replicate top 2 bits */
245
246 #define LBR02PacketRed(pixelpacket) \
247    { \
248      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
249      (pixelpacket).red=ScaleCharToQuantum( \
250        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
251    }
252 #define LBR02PacketGreen(pixelpacket) \
253    { \
254      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
255      (pixelpacket).green=ScaleCharToQuantum( \
256        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
257    }
258 #define LBR02PacketBlue(pixelpacket) \
259    { \
260      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
261      (pixelpacket).blue=ScaleCharToQuantum( \
262        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
263    }
264 #define LBR02PacketAlpha(pixelpacket) \
265    { \
266      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
267      (pixelpacket).alpha=ScaleCharToQuantum( \
268        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
269    }
270
271 #define LBR02PacketRGB(pixelpacket) \
272         { \
273         LBR02PacketRed((pixelpacket)); \
274         LBR02PacketGreen((pixelpacket)); \
275         LBR02PacketBlue((pixelpacket)); \
276         }
277
278 #define LBR02PacketRGBO(pixelpacket) \
279         { \
280         LBR02PacketRGB((pixelpacket)); \
281         LBR02PacketAlpha((pixelpacket)); \
282         }
283
284 #define LBR02PixelRed(pixel) \
285    { \
286      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
287        & 0xc0; \
288      SetPixelRed(image, ScaleCharToQuantum( \
289        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
290        (pixel)); \
291    }
292 #define LBR02PixelGreen(pixel) \
293    { \
294      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
295        & 0xc0; \
296      SetPixelGreen(image, ScaleCharToQuantum( \
297        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
298        (pixel)); \
299    }
300 #define LBR02PixelBlue(pixel) \
301    { \
302      unsigned char lbr_bits= \
303        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
304      SetPixelBlue(image, ScaleCharToQuantum( \
305        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
306        (pixel)); \
307    }
308 #define LBR02PixelAlpha(pixel) \
309    { \
310      unsigned char lbr_bits= \
311        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
312      SetPixelAlpha(image, ScaleCharToQuantum( \
313        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
314        (pixel) ); \
315    }
316
317 #define LBR02PixelRGB(pixel) \
318         { \
319         LBR02PixelRed((pixel)); \
320         LBR02PixelGreen((pixel)); \
321         LBR02PixelBlue((pixel)); \
322         }
323
324 #define LBR02PixelRGBA(pixel) \
325         { \
326         LBR02PixelRGB((pixel)); \
327         LBR02PixelAlpha((pixel)); \
328         }
329
330 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
331    PNG8 quantization) */
332
333 #define LBR03PacketRed(pixelpacket) \
334    { \
335      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
336      (pixelpacket).red=ScaleCharToQuantum( \
337        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
338    }
339 #define LBR03PacketGreen(pixelpacket) \
340    { \
341      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
342      (pixelpacket).green=ScaleCharToQuantum( \
343        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
344    }
345 #define LBR03PacketBlue(pixelpacket) \
346    { \
347      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
348      (pixelpacket).blue=ScaleCharToQuantum( \
349        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
350    }
351
352 #define LBR03PacketRGB(pixelpacket) \
353         { \
354         LBR03PacketRed((pixelpacket)); \
355         LBR03PacketGreen((pixelpacket)); \
356         LBR03PacketBlue((pixelpacket)); \
357         }
358
359 #define LBR03PixelRed(pixel) \
360    { \
361      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
362        & 0xe0; \
363      SetPixelRed(image, ScaleCharToQuantum( \
364        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
365    }
366 #define LBR03Green(pixel) \
367    { \
368      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
369        & 0xe0; \
370      SetPixelGreen(image, ScaleCharToQuantum( \
371        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
372    }
373 #define LBR03Blue(pixel) \
374    { \
375      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
376        & 0xe0; \
377      SetPixelBlue(image, ScaleCharToQuantum( \
378        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
379    }
380
381 #define LBR03RGB(pixel) \
382         { \
383         LBR03PixelRed((pixel)); \
384         LBR03Green((pixel)); \
385         LBR03Blue((pixel)); \
386         }
387
388 /* LBR04: Replicate top 4 bits */
389
390 #define LBR04PacketRed(pixelpacket) \
391    { \
392      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
393      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
394    }
395 #define LBR04PacketGreen(pixelpacket) \
396    { \
397      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
398      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
399    }
400 #define LBR04PacketBlue(pixelpacket) \
401    { \
402      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
403      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
404    }
405 #define LBR04PacketAlpha(pixelpacket) \
406    { \
407      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
408      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
409    }
410
411 #define LBR04PacketRGB(pixelpacket) \
412         { \
413         LBR04PacketRed((pixelpacket)); \
414         LBR04PacketGreen((pixelpacket)); \
415         LBR04PacketBlue((pixelpacket)); \
416         }
417
418 #define LBR04PacketRGBO(pixelpacket) \
419         { \
420         LBR04PacketRGB((pixelpacket)); \
421         LBR04PacketAlpha((pixelpacket)); \
422         }
423
424 #define LBR04PixelRed(pixel) \
425    { \
426      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
427        & 0xf0; \
428      SetPixelRed(image,\
429        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
430    }
431 #define LBR04PixelGreen(pixel) \
432    { \
433      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
434        & 0xf0; \
435      SetPixelGreen(image,\
436        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
437    }
438 #define LBR04PixelBlue(pixel) \
439    { \
440      unsigned char lbr_bits= \
441        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
442      SetPixelBlue(image,\
443        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
444    }
445 #define LBR04PixelAlpha(pixel) \
446    { \
447      unsigned char lbr_bits= \
448        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
449      SetPixelAlpha(image,\
450        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
451    }
452
453 #define LBR04PixelRGB(pixel) \
454         { \
455         LBR04PixelRed((pixel)); \
456         LBR04PixelGreen((pixel)); \
457         LBR04PixelBlue((pixel)); \
458         }
459
460 #define LBR04PixelRGBA(pixel) \
461         { \
462         LBR04PixelRGB((pixel)); \
463         LBR04PixelAlpha((pixel)); \
464         }
465
466 /*
467   Establish thread safety.
468   setjmp/longjmp is claimed to be safe on these platforms:
469   setjmp/longjmp is alleged to be unsafe on these platforms:
470 */
471 #ifdef PNG_SETJMP_SUPPORTED
472 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
473 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
474 # endif
475
476 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
477 static SemaphoreInfo
478   *ping_semaphore = (SemaphoreInfo *) NULL;
479 # endif
480 #endif
481
482 /*
483   This temporary until I set up malloc'ed object attributes array.
484   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
485   waste more memory.
486 */
487 #define MNG_MAX_OBJECTS 256
488
489 /*
490   If this not defined, spec is interpreted strictly.  If it is
491   defined, an attempt will be made to recover from some errors,
492   including
493       o global PLTE too short
494 */
495 #undef MNG_LOOSE
496
497 /*
498   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
499   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
500   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
501   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
502   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
503   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
504   will be enabled by default in libpng-1.2.0.
505 */
506 #ifdef PNG_MNG_FEATURES_SUPPORTED
507 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
508 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
509 #  endif
510 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
511 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
512 #  endif
513 #endif
514
515 /*
516   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
517   This macro is only defined in libpng-1.0.3 and later.
518   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
519 */
520 #ifndef PNG_UINT_31_MAX
521 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
522 #endif
523
524 /*
525   Constant strings for known chunk types.  If you need to add a chunk,
526   add a string holding the name here.   To make the code more
527   portable, we use ASCII numbers like this, not characters.
528 */
529
530 static const png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
531 static const png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
532 static const png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
533 static const png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
534 static const png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
535 static const png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
536 static const png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
537 static const png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
538 static const png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
539 static const png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
540 static const png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
541 static const png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
542 static const png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
543 static const png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
544 static const png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
545 static const png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
546 static const png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
547 static const png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
548 static const png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
549 static const png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
550 static const png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
551 static const png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
552 static const png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
553 static const png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
554 static const png_byte mng_caNv[5]={ 99,  97,  78, 118, (png_byte) '\0'};
555 static const png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
556 static const png_byte mng_eXIf[5]={101,  88,  73, 102, (png_byte) '\0'};
557 static const png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
558 static const png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
559 static const png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
560 static const png_byte mng_orNT[5]={111, 114,  78,  84, (png_byte) '\0'};
561 static const png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
562 static const png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
563 static const png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
564 static const png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
565 static const png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
566 static const png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
567
568 #if defined(JNG_SUPPORTED)
569 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
570 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
571 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
572 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
573 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
574 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
575 #endif
576
577 #if 0
578 /* Other known chunks that are not yet supported by ImageMagick: */
579 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
580 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
581 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
582 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
583 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
584 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
585 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
586 #endif
587
588 typedef struct _MngBox
589 {
590   long
591     left,
592     right,
593     top,
594     bottom;
595 } MngBox;
596
597 typedef struct _MngPair
598 {
599   volatile long
600     a,
601     b;
602 } MngPair;
603
604 #ifdef MNG_OBJECT_BUFFERS
605 typedef struct _MngBuffer
606 {
607
608   size_t
609     height,
610     width;
611
612   Image
613     *image;
614
615   png_color
616     plte[256];
617
618   int
619     reference_count;
620
621   unsigned char
622     alpha_sample_depth,
623     compression_method,
624     color_type,
625     concrete,
626     filter_method,
627     frozen,
628     image_type,
629     interlace_method,
630     pixel_sample_depth,
631     plte_length,
632     sample_depth,
633     viewable;
634 } MngBuffer;
635 #endif
636
637 typedef struct _MngInfo
638 {
639
640 #ifdef MNG_OBJECT_BUFFERS
641   MngBuffer
642     *ob[MNG_MAX_OBJECTS];
643 #endif
644
645   Image *
646     image;
647
648   RectangleInfo
649     page;
650
651   int
652     adjoin,
653 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
654     bytes_in_read_buffer,
655     found_empty_plte,
656 #endif
657     equal_backgrounds,
658     equal_chrms,
659     equal_gammas,
660 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
661     defined(PNG_MNG_FEATURES_SUPPORTED)
662     equal_palettes,
663 #endif
664     equal_physs,
665     equal_srgbs,
666     framing_mode,
667     have_global_bkgd,
668     have_global_chrm,
669     have_global_gama,
670     have_global_phys,
671     have_global_sbit,
672     have_global_srgb,
673     have_saved_bkgd_index,
674     have_write_global_chrm,
675     have_write_global_gama,
676     have_write_global_plte,
677     have_write_global_srgb,
678     need_fram,
679     object_id,
680     old_framing_mode,
681     saved_bkgd_index;
682
683   int
684     new_number_colors;
685
686   ssize_t
687     image_found,
688     loop_count[256],
689     loop_iteration[256],
690     scenes_found,
691     x_off[MNG_MAX_OBJECTS],
692     y_off[MNG_MAX_OBJECTS];
693
694   MngBox
695     clip,
696     frame,
697     image_box,
698     object_clip[MNG_MAX_OBJECTS];
699
700   unsigned char
701     /* These flags could be combined into one byte */
702     exists[MNG_MAX_OBJECTS],
703     frozen[MNG_MAX_OBJECTS],
704     loop_active[256],
705     invisible[MNG_MAX_OBJECTS],
706     viewable[MNG_MAX_OBJECTS];
707
708   MagickOffsetType
709     loop_jump[256];
710
711   png_colorp
712     global_plte;
713
714   png_color_8
715     global_sbit;
716
717   png_byte
718 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
719     read_buffer[8],
720 #endif
721     global_trns[256];
722
723   float
724     global_gamma;
725
726   ChromaticityInfo
727     global_chrm;
728
729   RenderingIntent
730     global_srgb_intent;
731
732   unsigned long
733     delay,
734     global_plte_length,
735     global_trns_length,
736     global_x_pixels_per_unit,
737     global_y_pixels_per_unit,
738     mng_width,
739     mng_height,
740     ticks_per_second;
741
742   MagickBooleanType
743     need_blob;
744
745   unsigned int
746     IsPalette,
747     global_phys_unit_type,
748     basi_warning,
749     clon_warning,
750     dhdr_warning,
751     jhdr_warning,
752     magn_warning,
753     past_warning,
754     phyg_warning,
755     phys_warning,
756     sbit_warning,
757     show_warning,
758     mng_type,
759     write_mng,
760     write_png_colortype,
761     write_png_depth,
762     write_png_compression_level,
763     write_png_compression_strategy,
764     write_png_compression_filter,
765     write_png8,
766     write_png24,
767     write_png32,
768     write_png48,
769     write_png64;
770
771 #ifdef MNG_BASI_SUPPORTED
772   unsigned long
773     basi_width,
774     basi_height;
775
776   unsigned int
777     basi_depth,
778     basi_color_type,
779     basi_compression_method,
780     basi_filter_type,
781     basi_interlace_method,
782     basi_red,
783     basi_green,
784     basi_blue,
785     basi_alpha,
786     basi_viewable;
787 #endif
788
789   png_uint_16
790     magn_first,
791     magn_last,
792     magn_mb,
793     magn_ml,
794     magn_mr,
795     magn_mt,
796     magn_mx,
797     magn_my,
798     magn_methx,
799     magn_methy;
800
801   PixelInfo
802     mng_global_bkgd;
803
804   /* Added at version 6.6.6-7 */
805   MagickBooleanType
806     ping_exclude_bKGD,
807     ping_exclude_cHRM,
808     ping_exclude_date,
809     ping_exclude_eXIf,
810     ping_exclude_EXIF,
811     ping_exclude_gAMA,
812     ping_exclude_iCCP,
813     /* ping_exclude_iTXt, */
814     ping_exclude_oFFs,
815     ping_exclude_pHYs,
816     ping_exclude_sRGB,
817     ping_exclude_tEXt,
818     ping_exclude_tRNS,
819     ping_exclude_caNv,
820     ping_exclude_zCCP, /* hex-encoded iCCP */
821     ping_exclude_zTXt,
822     ping_preserve_colormap,
823   /* Added at version 6.8.5-7 */
824     ping_preserve_iCCP,
825   /* Added at version 6.8.9-9 */
826     ping_exclude_tIME;
827
828 } MngInfo;
829 #endif /* VER */
830 \f
831 /*
832   Forward declarations.
833 */
834 static MagickBooleanType
835   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
836
837 static MagickBooleanType
838   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
839
840 #if defined(JNG_SUPPORTED)
841 static MagickBooleanType
842   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
843 #endif
844
845 #if PNG_LIBPNG_VER > 10011
846
847
848 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
849 static MagickBooleanType
850 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
851 {
852     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
853      *
854      * This is true if the high byte and the next highest byte of
855      * each sample of the image, the colormap, and the background color
856      * are equal to each other.  We check this by seeing if the samples
857      * are unchanged when we scale them down to 8 and back up to Quantum.
858      *
859      * We don't use the method GetImageDepth() because it doesn't check
860      * background and doesn't handle PseudoClass specially.
861      */
862
863 #define QuantumToCharToQuantumEqQuantum(quantum) \
864  ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
865
866     MagickBooleanType
867       ok_to_reduce=MagickFalse;
868
869     if (image->depth >= 16)
870       {
871
872         const Quantum
873           *p;
874
875         ok_to_reduce=
876            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
877            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
878            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
879            MagickTrue : MagickFalse;
880
881         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
882           {
883             int indx;
884
885             for (indx=0; indx < (ssize_t) image->colors; indx++)
886               {
887                 ok_to_reduce=(
888                    QuantumToCharToQuantumEqQuantum(
889                    image->colormap[indx].red) &&
890                    QuantumToCharToQuantumEqQuantum(
891                    image->colormap[indx].green) &&
892                    QuantumToCharToQuantumEqQuantum(
893                    image->colormap[indx].blue)) ?
894                    MagickTrue : MagickFalse;
895
896                 if (ok_to_reduce == MagickFalse)
897                    break;
898               }
899           }
900
901         if ((ok_to_reduce != MagickFalse) &&
902             (image->storage_class != PseudoClass))
903           {
904             ssize_t
905               y;
906
907             register ssize_t
908               x;
909
910             for (y=0; y < (ssize_t) image->rows; y++)
911             {
912               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
913
914               if (p == (const Quantum *) NULL)
915                 {
916                   ok_to_reduce = MagickFalse;
917                   break;
918                 }
919
920               for (x=(ssize_t) image->columns-1; x >= 0; x--)
921               {
922                 ok_to_reduce=
923                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
924                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
925                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
926                    MagickTrue : MagickFalse;
927
928                 if (ok_to_reduce == MagickFalse)
929                   break;
930
931                 p+=GetPixelChannels(image);
932               }
933               if (x >= 0)
934                 break;
935             }
936           }
937
938         if (ok_to_reduce != MagickFalse)
939           {
940             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
941                 "    OK to reduce PNG bit depth to 8 without loss of info");
942           }
943         else
944           {
945             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
946                 "    Not OK to reduce PNG bit depth to 8 without losing info");
947           }
948       }
949
950     return ok_to_reduce;
951 }
952 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
953
954 static const char* PngColorTypeToString(const unsigned int color_type)
955 {
956   const char
957     *result = "Unknown";
958
959   switch (color_type)
960     {
961     case PNG_COLOR_TYPE_GRAY:
962       result = "Gray";
963       break;
964     case PNG_COLOR_TYPE_GRAY_ALPHA:
965       result = "Gray+Alpha";
966       break;
967     case PNG_COLOR_TYPE_PALETTE:
968       result = "Palette";
969       break;
970     case PNG_COLOR_TYPE_RGB:
971       result = "RGB";
972       break;
973     case PNG_COLOR_TYPE_RGB_ALPHA:
974       result = "RGB+Alpha";
975       break;
976     }
977
978   return result;
979 }
980
981 static int
982 Magick_Orientation_to_Exif_Orientation(const OrientationType orientation)
983 {
984   switch (orientation)
985   {
986     /* Convert to Exif orientations as defined in "Exchangeable image file
987      * format for digital still cameras: Exif Version 2.31",
988      * http://www.cipa.jp/std/documents/e/DC-008-Translation-2016-E.pdf
989      */
990
991     case TopLeftOrientation:
992        return 1;
993     case TopRightOrientation:
994        return 2;
995     case BottomRightOrientation:
996        return 3;
997     case BottomLeftOrientation:
998        return 4;
999     case LeftTopOrientation:
1000        return 5;
1001     case RightTopOrientation:
1002        return 6;
1003     case RightBottomOrientation:
1004        return 7;
1005     case LeftBottomOrientation:
1006        return 8;
1007     case UndefinedOrientation:
1008     default:
1009        return 0;
1010   }
1011 }
1012 static OrientationType
1013 Magick_Orientation_from_Exif_Orientation(const int orientation)
1014 {
1015   switch (orientation)
1016   {
1017     case 1:
1018       return TopLeftOrientation;
1019     case 2:
1020       return TopRightOrientation;
1021     case 3:
1022       return BottomRightOrientation;
1023     case 4:
1024       return BottomLeftOrientation;
1025     case 5:
1026       return LeftTopOrientation;
1027     case 6:
1028       return RightTopOrientation;
1029     case 7:
1030       return RightBottomOrientation;
1031     case 8:
1032       return LeftBottomOrientation;
1033     case 0:
1034     default:
1035       return UndefinedOrientation;
1036   }
1037 }
1038
1039 static int
1040 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
1041 {
1042   switch (intent)
1043   {
1044     case PerceptualIntent:
1045        return 0;
1046
1047     case RelativeIntent:
1048        return 1;
1049
1050     case SaturationIntent:
1051        return 2;
1052
1053     case AbsoluteIntent:
1054        return 3;
1055
1056     default:
1057        return -1;
1058   }
1059 }
1060
1061 static RenderingIntent
1062 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1063 {
1064   switch (ping_intent)
1065   {
1066     case 0:
1067       return PerceptualIntent;
1068
1069     case 1:
1070       return RelativeIntent;
1071
1072     case 2:
1073       return SaturationIntent;
1074
1075     case 3:
1076       return AbsoluteIntent;
1077
1078     default:
1079       return UndefinedIntent;
1080     }
1081 }
1082
1083 static const char *
1084 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1085 {
1086   switch (ping_intent)
1087   {
1088     case 0:
1089       return "Perceptual Intent";
1090
1091     case 1:
1092       return "Relative Intent";
1093
1094     case 2:
1095       return "Saturation Intent";
1096
1097     case 3:
1098       return "Absolute Intent";
1099
1100     default:
1101       return "Undefined Intent";
1102     }
1103 }
1104
1105 static const char *
1106 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1107 {
1108   switch (ping_colortype)
1109   {
1110     case 0:
1111       return "Grayscale";
1112
1113     case 2:
1114       return "Truecolor";
1115
1116     case 3:
1117       return "Indexed";
1118
1119     case 4:
1120       return "GrayAlpha";
1121
1122     case 6:
1123       return "RGBA";
1124
1125     default:
1126       return "UndefinedColorType";
1127     }
1128 }
1129
1130 #endif /* PNG_LIBPNG_VER > 10011 */
1131 #endif /* MAGICKCORE_PNG_DELEGATE */
1132 \f
1133 /*
1134 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1135 %                                                                             %
1136 %                                                                             %
1137 %                                                                             %
1138 %   I s M N G                                                                 %
1139 %                                                                             %
1140 %                                                                             %
1141 %                                                                             %
1142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1143 %
1144 %  IsMNG() returns MagickTrue if the image format type, identified by the
1145 %  magick string, is MNG.
1146 %
1147 %  The format of the IsMNG method is:
1148 %
1149 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1150 %
1151 %  A description of each parameter follows:
1152 %
1153 %    o magick: compare image format pattern against these bytes.
1154 %
1155 %    o length: Specifies the length of the magick string.
1156 %
1157 %
1158 */
1159 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1160 {
1161   if (length < 8)
1162     return(MagickFalse);
1163
1164   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1165     return(MagickTrue);
1166
1167   return(MagickFalse);
1168 }
1169 \f
1170 /*
1171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1172 %                                                                             %
1173 %                                                                             %
1174 %                                                                             %
1175 %   I s J N G                                                                 %
1176 %                                                                             %
1177 %                                                                             %
1178 %                                                                             %
1179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1180 %
1181 %  IsJNG() returns MagickTrue if the image format type, identified by the
1182 %  magick string, is JNG.
1183 %
1184 %  The format of the IsJNG method is:
1185 %
1186 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1187 %
1188 %  A description of each parameter follows:
1189 %
1190 %    o magick: compare image format pattern against these bytes.
1191 %
1192 %    o length: Specifies the length of the magick string.
1193 %
1194 %
1195 */
1196 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1197 {
1198   if (length < 8)
1199     return(MagickFalse);
1200
1201   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1202     return(MagickTrue);
1203
1204   return(MagickFalse);
1205 }
1206 \f
1207 /*
1208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1209 %                                                                             %
1210 %                                                                             %
1211 %                                                                             %
1212 %   I s P N G                                                                 %
1213 %                                                                             %
1214 %                                                                             %
1215 %                                                                             %
1216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1217 %
1218 %  IsPNG() returns MagickTrue if the image format type, identified by the
1219 %  magick string, is PNG.
1220 %
1221 %  The format of the IsPNG method is:
1222 %
1223 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1224 %
1225 %  A description of each parameter follows:
1226 %
1227 %    o magick: compare image format pattern against these bytes.
1228 %
1229 %    o length: Specifies the length of the magick string.
1230 %
1231 */
1232 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1233 {
1234   if (length < 8)
1235     return(MagickFalse);
1236
1237   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1238     return(MagickTrue);
1239
1240   return(MagickFalse);
1241 }
1242 \f
1243 #if defined(MAGICKCORE_PNG_DELEGATE)
1244 #if defined(__cplusplus) || defined(c_plusplus)
1245 extern "C" {
1246 #endif
1247
1248 #if (PNG_LIBPNG_VER > 10011)
1249 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1250 {
1251   unsigned char
1252     buffer[4];
1253
1254   assert(image != (Image *) NULL);
1255   assert(image->signature == MagickCoreSignature);
1256   buffer[0]=(unsigned char) (value >> 24);
1257   buffer[1]=(unsigned char) (value >> 16);
1258   buffer[2]=(unsigned char) (value >> 8);
1259   buffer[3]=(unsigned char) value;
1260   return((size_t) WriteBlob(image,4,buffer));
1261 }
1262
1263 static void PNGLong(png_bytep p,png_uint_32 value)
1264 {
1265   *p++=(png_byte) ((value >> 24) & 0xff);
1266   *p++=(png_byte) ((value >> 16) & 0xff);
1267   *p++=(png_byte) ((value >> 8) & 0xff);
1268   *p++=(png_byte) (value & 0xff);
1269 }
1270
1271 static void PNGsLong(png_bytep p,png_int_32 value)
1272 {
1273   *p++=(png_byte) ((value >> 24) & 0xff);
1274   *p++=(png_byte) ((value >> 16) & 0xff);
1275   *p++=(png_byte) ((value >> 8) & 0xff);
1276   *p++=(png_byte) (value & 0xff);
1277 }
1278
1279 static void PNGShort(png_bytep p,png_uint_16 value)
1280 {
1281   *p++=(png_byte) ((value >> 8) & 0xff);
1282   *p++=(png_byte) (value & 0xff);
1283 }
1284
1285 static void PNGType(png_bytep p,const png_byte *type)
1286 {
1287   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1288 }
1289
1290 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1291    size_t length)
1292 {
1293   if (logging != MagickFalse)
1294     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1295       "  Writing %c%c%c%c chunk, length: %.20g",
1296       type[0],type[1],type[2],type[3],(double) length);
1297 }
1298 #endif /* PNG_LIBPNG_VER > 10011 */
1299
1300 #if defined(__cplusplus) || defined(c_plusplus)
1301 }
1302 #endif
1303
1304 #if PNG_LIBPNG_VER > 10011
1305 /*
1306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1307 %                                                                             %
1308 %                                                                             %
1309 %                                                                             %
1310 %   R e a d P N G I m a g e                                                   %
1311 %                                                                             %
1312 %                                                                             %
1313 %                                                                             %
1314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1315 %
1316 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1317 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1318 %  allocates the memory necessary for the new Image structure and returns a
1319 %  pointer to the new image or set of images.
1320 %
1321 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1322 %
1323 %  The format of the ReadPNGImage method is:
1324 %
1325 %     Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1326 %
1327 %  A description of each parameter follows:
1328 %
1329 %    o image_info: the image info.
1330 %
1331 %    o exception: return any errors or warnings in this structure.
1332 %
1333 %  To do, more or less in chronological order (as of version 5.5.2,
1334 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1335 %
1336 %    Get 16-bit cheap transparency working.
1337 %
1338 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1339 %
1340 %    Preserve all unknown and not-yet-handled known chunks found in input
1341 %    PNG file and copy them into output PNG files according to the PNG
1342 %    copying rules.
1343 %
1344 %    (At this point, PNG encoding should be in full MNG compliance)
1345 %
1346 %    Provide options for choice of background to use when the MNG BACK
1347 %    chunk is not present or is not mandatory (i.e., leave transparent,
1348 %    user specified, MNG BACK, PNG bKGD)
1349 %
1350 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1351 %    efficiently by linking in the duplicate frames.].
1352 %
1353 %    Decode and act on the MHDR simplicity profile (offer option to reject
1354 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1355 %
1356 %    Upgrade to full MNG without Delta-PNG.
1357 %
1358 %        o  BACK [done a while ago except for background image ID]
1359 %        o  MOVE [done 15 May 1999]
1360 %        o  CLIP [done 15 May 1999]
1361 %        o  DISC [done 19 May 1999]
1362 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1363 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1364 %        o  SHOW
1365 %        o  PAST
1366 %        o  BASI
1367 %        o  MNG-level tEXt/iTXt/zTXt
1368 %        o  pHYg
1369 %        o  pHYs
1370 %        o  sBIT
1371 %        o  bKGD
1372 %        o  iTXt (wait for libpng implementation).
1373 %
1374 %    Use the scene signature to discover when an identical scene is
1375 %    being reused, and just point to the original image->exception instead
1376 %    of storing another set of pixels.  This not specific to MNG
1377 %    but could be applied generally.
1378 %
1379 %    Upgrade to full MNG with Delta-PNG.
1380 %
1381 %    JNG tEXt/iTXt/zTXt
1382 %
1383 %    We will not attempt to read files containing the CgBI chunk.
1384 %    They are really Xcode files meant for display on the iPhone.
1385 %    These are not valid PNG files and it is impossible to recover
1386 %    the original PNG from files that have been converted to Xcode-PNG,
1387 %    since irretrievable loss of color data has occurred due to the
1388 %    use of premultiplied alpha.
1389 */
1390
1391 #if defined(__cplusplus) || defined(c_plusplus)
1392 extern "C" {
1393 #endif
1394
1395 /*
1396   This the function that does the actual reading of data.  It is
1397   the same as the one supplied in libpng, except that it receives the
1398   datastream from the ReadBlob() function instead of standard input.
1399 */
1400 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1401 {
1402   Image
1403     *image;
1404
1405   image=(Image *) png_get_io_ptr(png_ptr);
1406   if (length != 0)
1407     {
1408       png_size_t
1409         check;
1410
1411       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1412       if (check != length)
1413         {
1414           char
1415             msg[MagickPathExtent];
1416
1417           (void) FormatLocaleString(msg,MagickPathExtent,
1418             "Expected %.20g bytes; found %.20g bytes",(double) length,
1419             (double) check);
1420           png_warning(png_ptr,msg);
1421           png_error(png_ptr,"Read Exception");
1422         }
1423     }
1424 }
1425
1426 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1427     !defined(PNG_MNG_FEATURES_SUPPORTED)
1428 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1429  * older than libpng-1.0.3a, which was the first to allow the empty
1430  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1431  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1432  * encountered after an empty PLTE, so we have to look ahead for bKGD
1433  * chunks and remove them from the datastream that is passed to libpng,
1434  * and store their contents for later use.
1435  */
1436 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1437 {
1438   MngInfo
1439     *mng_info;
1440
1441   Image
1442     *image;
1443
1444   png_size_t
1445     check;
1446
1447   register ssize_t
1448     i;
1449
1450   i=0;
1451   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1452   image=(Image *) mng_info->image;
1453   while (mng_info->bytes_in_read_buffer && length)
1454   {
1455     data[i]=mng_info->read_buffer[i];
1456     mng_info->bytes_in_read_buffer--;
1457     length--;
1458     i++;
1459   }
1460   if (length != 0)
1461     {
1462       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1463
1464       if (check != length)
1465         png_error(png_ptr,"Read Exception");
1466
1467       if (length == 4)
1468         {
1469           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1470               (data[3] == 0))
1471             {
1472               check=(png_size_t) ReadBlob(image,(size_t) length,
1473                 (char *) mng_info->read_buffer);
1474               mng_info->read_buffer[4]=0;
1475               mng_info->bytes_in_read_buffer=4;
1476               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1477                 mng_info->found_empty_plte=MagickTrue;
1478               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1479                 {
1480                   mng_info->found_empty_plte=MagickFalse;
1481                   mng_info->have_saved_bkgd_index=MagickFalse;
1482                 }
1483             }
1484
1485           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1486               (data[3] == 1))
1487             {
1488               check=(png_size_t) ReadBlob(image,(size_t) length,
1489                 (char *) mng_info->read_buffer);
1490               mng_info->read_buffer[4]=0;
1491               mng_info->bytes_in_read_buffer=4;
1492               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1493                 if (mng_info->found_empty_plte)
1494                   {
1495                     /*
1496                       Skip the bKGD data byte and CRC.
1497                     */
1498                     check=(png_size_t)
1499                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1500                     check=(png_size_t) ReadBlob(image,(size_t) length,
1501                       (char *) mng_info->read_buffer);
1502                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1503                     mng_info->have_saved_bkgd_index=MagickTrue;
1504                     mng_info->bytes_in_read_buffer=0;
1505                   }
1506             }
1507         }
1508     }
1509 }
1510 #endif
1511
1512 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1513 {
1514   Image
1515     *image;
1516
1517   image=(Image *) png_get_io_ptr(png_ptr);
1518   if (length != 0)
1519     {
1520       png_size_t
1521         check;
1522
1523       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1524
1525       if (check != length)
1526         png_error(png_ptr,"WriteBlob Failed");
1527     }
1528 }
1529
1530 static void png_flush_data(png_structp png_ptr)
1531 {
1532   (void) png_ptr;
1533 }
1534
1535 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1536 static int PalettesAreEqual(Image *a,Image *b)
1537 {
1538   ssize_t
1539     i;
1540
1541   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1542     return((int) MagickFalse);
1543
1544   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1545     return((int) MagickFalse);
1546
1547   if (a->colors != b->colors)
1548     return((int) MagickFalse);
1549
1550   for (i=0; i < (ssize_t) a->colors; i++)
1551   {
1552     if ((a->colormap[i].red != b->colormap[i].red) ||
1553         (a->colormap[i].green != b->colormap[i].green) ||
1554         (a->colormap[i].blue != b->colormap[i].blue))
1555       return((int) MagickFalse);
1556   }
1557
1558   return((int) MagickTrue);
1559 }
1560 #endif
1561
1562 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1563 {
1564   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1565       mng_info->exists[i] && !mng_info->frozen[i])
1566     {
1567 #ifdef MNG_OBJECT_BUFFERS
1568       if (mng_info->ob[i] != (MngBuffer *) NULL)
1569         {
1570           if (mng_info->ob[i]->reference_count > 0)
1571             mng_info->ob[i]->reference_count--;
1572
1573           if (mng_info->ob[i]->reference_count == 0)
1574             {
1575               if (mng_info->ob[i]->image != (Image *) NULL)
1576                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1577
1578               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1579             }
1580         }
1581       mng_info->ob[i]=(MngBuffer *) NULL;
1582 #endif
1583       mng_info->exists[i]=MagickFalse;
1584       mng_info->invisible[i]=MagickFalse;
1585       mng_info->viewable[i]=MagickFalse;
1586       mng_info->frozen[i]=MagickFalse;
1587       mng_info->x_off[i]=0;
1588       mng_info->y_off[i]=0;
1589       mng_info->object_clip[i].left=0;
1590       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1591       mng_info->object_clip[i].top=0;
1592       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1593     }
1594 }
1595
1596 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1597 {
1598   register ssize_t
1599     i;
1600
1601   if (mng_info == (MngInfo *) NULL)
1602     return((MngInfo *) NULL);
1603
1604   for (i=1; i < MNG_MAX_OBJECTS; i++)
1605     MngInfoDiscardObject(mng_info,i);
1606
1607   mng_info->global_plte=(png_colorp)
1608     RelinquishMagickMemory(mng_info->global_plte);
1609
1610   return((MngInfo *) RelinquishMagickMemory(mng_info));
1611 }
1612
1613 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1614 {
1615   MngBox
1616     box;
1617
1618   box=box1;
1619   if (box.left < box2.left)
1620     box.left=box2.left;
1621
1622   if (box.top < box2.top)
1623     box.top=box2.top;
1624
1625   if (box.right > box2.right)
1626     box.right=box2.right;
1627
1628   if (box.bottom > box2.bottom)
1629     box.bottom=box2.bottom;
1630
1631   return box;
1632 }
1633
1634 static long mng_get_long(unsigned char *p)
1635 {
1636   return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1637     ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1638 }
1639
1640 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1641   unsigned char *p)
1642 {
1643    MngBox
1644       box;
1645
1646   /*
1647     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1648   */
1649   box.left=mng_get_long(p);
1650   box.right=mng_get_long(&p[4]);
1651   box.top=mng_get_long(&p[8]);
1652   box.bottom=mng_get_long(&p[12]);
1653   if (delta_type != 0)
1654     {
1655       box.left+=previous_box.left;
1656       box.right+=previous_box.right;
1657       box.top+=previous_box.top;
1658       box.bottom+=previous_box.bottom;
1659     }
1660
1661   return(box);
1662 }
1663
1664 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1665   unsigned char *p)
1666 {
1667   MngPair
1668     pair;
1669
1670   /*
1671     Read two ssize_t's from CLON, MOVE or PAST chunk
1672   */
1673   pair.a=mng_get_long(p);
1674   pair.b=mng_get_long(&p[4]);
1675   if (delta_type != 0)
1676     {
1677       pair.a+=previous_pair.a;
1678       pair.b+=previous_pair.b;
1679     }
1680
1681   return(pair);
1682 }
1683
1684 typedef struct _PNGErrorInfo
1685 {
1686   Image
1687     *image;
1688
1689   ExceptionInfo
1690     *exception;
1691 } PNGErrorInfo;
1692
1693 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1694 {
1695   ExceptionInfo
1696     *exception;
1697
1698   Image
1699     *image;
1700
1701   PNGErrorInfo
1702     *error_info;
1703
1704   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1705   image=error_info->image;
1706   exception=error_info->exception;
1707
1708   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1709     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1710
1711   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1712     "`%s'",image->filename);
1713
1714 #if (PNG_LIBPNG_VER < 10500)
1715   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1716    * are building with libpng-1.4.x and can be ignored.
1717    */
1718   longjmp(ping->jmpbuf,1);
1719 #else
1720   png_longjmp(ping,1);
1721 #endif
1722 }
1723
1724 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1725 {
1726   ExceptionInfo
1727     *exception;
1728
1729   Image
1730     *image;
1731
1732   PNGErrorInfo
1733     *error_info;
1734
1735   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1736     png_error(ping, message);
1737
1738   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1739   image=error_info->image;
1740   exception=error_info->exception;
1741   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1742     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1743
1744   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1745     message,"`%s'",image->filename);
1746 }
1747
1748 #ifdef PNG_USER_MEM_SUPPORTED
1749 #if PNG_LIBPNG_VER >= 10400
1750 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1751 #else
1752 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1753 #endif
1754 {
1755   (void) png_ptr;
1756   return((png_voidp) AcquireMagickMemory((size_t) size));
1757 }
1758
1759 /*
1760   Free a pointer.  It is removed from the list at the same time.
1761 */
1762 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1763 {
1764   (void) png_ptr;
1765   ptr=RelinquishMagickMemory(ptr);
1766   return((png_free_ptr) NULL);
1767 }
1768 #endif
1769
1770 #if defined(__cplusplus) || defined(c_plusplus)
1771 }
1772 #endif
1773
1774 static int
1775 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1776    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1777 {
1778   register ssize_t
1779     i;
1780
1781   register unsigned char
1782     *dp;
1783
1784   register png_charp
1785     sp;
1786
1787   png_uint_32
1788     length,
1789     nibbles;
1790
1791   StringInfo
1792     *profile;
1793
1794   const unsigned char
1795     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1796                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1797                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1798                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1799                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1800                  13,14,15};
1801
1802   sp=text[ii].text+1;
1803   /* look for newline */
1804   while (*sp != '\n')
1805      sp++;
1806
1807   /* look for length */
1808   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1809      sp++;
1810
1811   length=(png_uint_32) StringToLong(sp);
1812
1813   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1814        "      length: %lu",(unsigned long) length);
1815
1816   while (*sp != ' ' && *sp != '\n')
1817      sp++;
1818
1819   /* allocate space */
1820   if (length == 0)
1821   {
1822     png_warning(ping,"invalid profile length");
1823     return(MagickFalse);
1824   }
1825
1826   profile=BlobToStringInfo((const void *) NULL,length);
1827
1828   if (profile == (StringInfo *) NULL)
1829   {
1830     png_warning(ping, "unable to copy profile");
1831     return(MagickFalse);
1832   }
1833
1834   /* copy profile, skipping white space and column 1 "=" signs */
1835   dp=GetStringInfoDatum(profile);
1836   nibbles=length*2;
1837
1838   for (i=0; i < (ssize_t) nibbles; i++)
1839   {
1840     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1841     {
1842       if (*sp == '\0')
1843         {
1844           png_warning(ping, "ran out of profile data");
1845           profile=DestroyStringInfo(profile);
1846           return(MagickFalse);
1847         }
1848       sp++;
1849     }
1850
1851     if (i%2 == 0)
1852       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1853
1854     else
1855       (*dp++)+=unhex[(int) *sp++];
1856   }
1857   /*
1858     We have already read "Raw profile type.
1859   */
1860   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1861   profile=DestroyStringInfo(profile);
1862
1863   if (image_info->verbose)
1864     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1865
1866   return MagickTrue;
1867 }
1868
1869 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1870
1871 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1872 {
1873   Image
1874     *image;
1875
1876
1877   /* The unknown chunk structure contains the chunk data:
1878      png_byte name[5];
1879      png_byte *data;
1880      png_size_t size;
1881
1882      Note that libpng has already taken care of the CRC handling.
1883
1884      Returns one of the following:
1885          return(-n);  chunk had an error
1886          return(0);  did not recognize
1887          return(n);  success
1888   */
1889
1890   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1891      "    read_user_chunk: found %c%c%c%c chunk",
1892        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1893
1894   if (chunk->name[0]  == 101 &&
1895       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
1896       chunk->name[2] ==   73 &&
1897       chunk-> name[3] == 102)
1898     {
1899       /* process eXIf or exIf chunk */
1900
1901       PNGErrorInfo
1902         *error_info;
1903
1904       StringInfo
1905         *profile;
1906
1907       unsigned char
1908         *p;
1909
1910       png_byte
1911         *s;
1912
1913       size_t
1914         i;
1915
1916       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1917         " recognized eXIf chunk");
1918
1919       image=(Image *) png_get_user_chunk_ptr(ping);
1920
1921       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1922
1923       profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1924
1925       if (profile == (StringInfo *) NULL)
1926         {
1927           (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1928             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1929             image->filename);
1930           return(-1);
1931         }
1932       p=GetStringInfoDatum(profile);
1933
1934       if (*p != 'E')
1935         {
1936           /* Initialize profile with "Exif\0\0" if it is not
1937              already present by accident
1938           */
1939           *p++ ='E';
1940           *p++ ='x';
1941           *p++ ='i';
1942           *p++ ='f';
1943           *p++ ='\0';
1944           *p++ ='\0';
1945         }
1946       else
1947         {
1948           if (p[1] != 'x' || p[2] != 'i' || p[3] != 'f' ||
1949               p[4] != '\0' || p[5] != '\0')
1950             {
1951               /* Chunk is malformed */
1952               profile=DestroyStringInfo(profile);
1953               return(-1);
1954             }
1955          }
1956
1957       /* copy chunk->data to profile */
1958       s=chunk->data;
1959       for (i=0; i<chunk->size; i++)
1960         *p++ = *s++;
1961
1962       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1963       (void) SetImageProfile(image,"exif",profile,
1964         error_info->exception);
1965
1966       profile=DestroyStringInfo(profile);
1967
1968       return(1);
1969     }
1970
1971   /* orNT */
1972   if (chunk->name[0] == 111 &&
1973       chunk->name[1] == 114 &&
1974       chunk->name[2] ==  78 &&
1975       chunk->name[3] ==  84)
1976     {
1977      /* recognized orNT */
1978      if (chunk->size != 1)
1979        return(-1); /* Error return */
1980
1981      image=(Image *) png_get_user_chunk_ptr(ping);
1982
1983      image->orientation=
1984        Magick_Orientation_from_Exif_Orientation((int) chunk->data[0]);
1985
1986      return(1);
1987     }
1988
1989   /* vpAg (deprecated, replaced by caNv) */
1990   if (chunk->name[0] == 118 &&
1991       chunk->name[1] == 112 &&
1992       chunk->name[2] ==  65 &&
1993       chunk->name[3] == 103)
1994     {
1995       /* recognized vpAg */
1996
1997       if (chunk->size != 9)
1998         return(-1); /* Error return */
1999
2000       if (chunk->data[8] != 0)
2001         return(0);  /* ImageMagick requires pixel units */
2002
2003       image=(Image *) png_get_user_chunk_ptr(ping);
2004
2005       image->page.width=(size_t)mng_get_long(chunk->data);
2006       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2007
2008       return(1);
2009     }
2010
2011   /* caNv */
2012   if (chunk->name[0] ==  99 &&
2013       chunk->name[1] ==  97 &&
2014       chunk->name[2] ==  78 &&
2015       chunk->name[3] == 118)
2016     {
2017       /* recognized caNv */
2018
2019       if (chunk->size != 16)
2020         return(-1); /* Error return */
2021
2022       image=(Image *) png_get_user_chunk_ptr(ping);
2023
2024       image->page.width=(size_t)mng_get_long(chunk->data);
2025       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
2026       image->page.x=(size_t)mng_get_long(&chunk->data[8]);
2027       image->page.y=(size_t)mng_get_long(&chunk->data[12]);
2028
2029       return(1);
2030     }
2031
2032   return(0); /* Did not recognize */
2033 }
2034 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
2035
2036 #if defined(PNG_tIME_SUPPORTED)
2037 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
2038   ExceptionInfo *exception)
2039 {
2040   png_timep
2041     time;
2042
2043   if (png_get_tIME(ping,info,&time))
2044     {
2045       char
2046         timestamp[21];
2047
2048       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
2049         time->year,time->month,time->day,time->hour,time->minute,time->second);
2050       SetImageProperty(image,"png:tIME",timestamp,exception);
2051     }
2052 }
2053 #endif
2054
2055 /*
2056 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2057 %                                                                             %
2058 %                                                                             %
2059 %                                                                             %
2060 %   R e a d O n e P N G I m a g e                                             %
2061 %                                                                             %
2062 %                                                                             %
2063 %                                                                             %
2064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2065 %
2066 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
2067 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
2068 %  necessary for the new Image structure and returns a pointer to the new
2069 %  image.
2070 %
2071 %  The format of the ReadOnePNGImage method is:
2072 %
2073 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
2074 %         ExceptionInfo *exception)
2075 %
2076 %  A description of each parameter follows:
2077 %
2078 %    o mng_info: Specifies a pointer to a MngInfo structure.
2079 %
2080 %    o image_info: the image info.
2081 %
2082 %    o exception: return any errors or warnings in this structure.
2083 %
2084 */
2085 static Image *ReadOnePNGImage(MngInfo *mng_info,
2086     const ImageInfo *image_info, ExceptionInfo *exception)
2087 {
2088   /* Read one PNG image */
2089
2090   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2091
2092   Image
2093     *image;
2094
2095   char
2096     im_vers[32],
2097     libpng_runv[32],
2098     libpng_vers[32],
2099     zlib_runv[32],
2100     zlib_vers[32];
2101
2102   int
2103     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2104     num_raw_profiles,
2105     num_text,
2106     num_text_total,
2107     num_passes,
2108     number_colors,
2109     pass,
2110     ping_bit_depth,
2111     ping_color_type,
2112     ping_file_depth,
2113     ping_interlace_method,
2114     ping_compression_method,
2115     ping_filter_method,
2116     ping_num_trans,
2117     unit_type;
2118
2119   double
2120     file_gamma;
2121
2122   MagickBooleanType
2123     logging,
2124     ping_found_cHRM,
2125     ping_found_gAMA,
2126     ping_found_iCCP,
2127     ping_found_sRGB,
2128     ping_found_sRGB_cHRM,
2129     ping_preserve_iCCP,
2130     status;
2131
2132   MemoryInfo
2133     *volatile pixel_info;
2134
2135   PixelInfo
2136     transparent_color;
2137
2138   PNGErrorInfo
2139     error_info;
2140
2141   png_bytep
2142      ping_trans_alpha;
2143
2144   png_color_16p
2145      ping_background,
2146      ping_trans_color;
2147
2148   png_info
2149     *end_info,
2150     *ping_info;
2151
2152   png_struct
2153     *ping;
2154
2155   png_textp
2156     text;
2157
2158   png_uint_32
2159     ping_height,
2160     ping_width,
2161     x_resolution,
2162     y_resolution;
2163
2164   QuantumInfo
2165     *volatile quantum_info;
2166
2167   Quantum
2168     *volatile quantum_scanline;
2169
2170   ssize_t
2171     ping_rowbytes,
2172     y;
2173
2174   register unsigned char
2175     *p;
2176
2177   register ssize_t
2178     i,
2179     x;
2180
2181   register Quantum
2182     *q;
2183
2184   size_t
2185     length,
2186     row_offset;
2187
2188   ssize_t
2189     j;
2190
2191   unsigned char
2192     *ping_pixels;
2193
2194 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2195   png_byte unused_chunks[]=
2196   {
2197     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2198     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2199     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2200     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2201     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2202 #if !defined(PNG_tIME_SUPPORTED)
2203     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2204 #endif
2205 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2206                           /* ignore the APNG chunks */
2207      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2208     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2209     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2210 #endif
2211   };
2212 #endif
2213
2214   /* Define these outside of the following "if logging()" block so they will
2215    * show in debuggers.
2216    */
2217   *im_vers='\0';
2218   (void) ConcatenateMagickString(im_vers,
2219          MagickLibVersionText,32);
2220   (void) ConcatenateMagickString(im_vers,
2221          MagickLibAddendum,32);
2222
2223   *libpng_vers='\0';
2224   (void) ConcatenateMagickString(libpng_vers,
2225          PNG_LIBPNG_VER_STRING,32);
2226   *libpng_runv='\0';
2227   (void) ConcatenateMagickString(libpng_runv,
2228          png_get_libpng_ver(NULL),32);
2229
2230   *zlib_vers='\0';
2231   (void) ConcatenateMagickString(zlib_vers,
2232          ZLIB_VERSION,32);
2233   *zlib_runv='\0';
2234   (void) ConcatenateMagickString(zlib_runv,
2235          zlib_version,32);
2236
2237   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2238        "  Enter ReadOnePNGImage()\n"
2239        "    IM version     = %s\n"
2240        "    Libpng version = %s",
2241        im_vers, libpng_vers);
2242
2243   if (logging != MagickFalse)
2244   {
2245     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2246     {
2247    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2248       "      running with   %s", libpng_runv);
2249     }
2250     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2251         "    Zlib version   = %s", zlib_vers);
2252     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2253     {
2254     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2255         "      running with   %s", zlib_runv);
2256     }
2257   }
2258
2259 #if (PNG_LIBPNG_VER < 10200)
2260   if (image_info->verbose)
2261     printf("Your PNG library (libpng-%s) is rather old.\n",
2262        PNG_LIBPNG_VER_STRING);
2263 #endif
2264
2265 #if (PNG_LIBPNG_VER >= 10400)
2266 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2267   if (image_info->verbose)
2268     {
2269       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2270            PNG_LIBPNG_VER_STRING);
2271       printf("Please update it.\n");
2272     }
2273 #  endif
2274 #endif
2275
2276
2277   quantum_info = (QuantumInfo *) NULL;
2278   image=mng_info->image;
2279
2280   if (logging != MagickFalse)
2281   {
2282     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2283        "    Before reading:\n"
2284        "      image->alpha_trait=%d\n"
2285        "      image->rendering_intent=%d\n"
2286        "      image->colorspace=%d\n"
2287        "      image->gamma=%f",
2288        (int) image->alpha_trait, (int) image->rendering_intent,
2289        (int) image->colorspace, image->gamma);
2290   }
2291   intent=
2292     Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2293
2294   /* Set to an out-of-range color unless tRNS chunk is present */
2295   transparent_color.red=65537;
2296   transparent_color.green=65537;
2297   transparent_color.blue=65537;
2298   transparent_color.alpha=65537;
2299
2300   number_colors=0;
2301   num_text = 0;
2302   num_text_total = 0;
2303   num_raw_profiles = 0;
2304
2305   ping_found_cHRM = MagickFalse;
2306   ping_found_gAMA = MagickFalse;
2307   ping_found_iCCP = MagickFalse;
2308   ping_found_sRGB = MagickFalse;
2309   ping_found_sRGB_cHRM = MagickFalse;
2310   ping_preserve_iCCP = MagickFalse;
2311
2312
2313   /*
2314     Allocate the PNG structures
2315   */
2316 #ifdef PNG_USER_MEM_SUPPORTED
2317  error_info.image=image;
2318  error_info.exception=exception;
2319  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2320    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2321    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2322 #else
2323   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2324     MagickPNGErrorHandler,MagickPNGWarningHandler);
2325 #endif
2326   if (ping == (png_struct *) NULL)
2327     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2328
2329   ping_info=png_create_info_struct(ping);
2330
2331   if (ping_info == (png_info *) NULL)
2332     {
2333       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2334       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2335     }
2336
2337   end_info=png_create_info_struct(ping);
2338
2339   if (end_info == (png_info *) NULL)
2340     {
2341       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2342       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2343     }
2344
2345   pixel_info=(MemoryInfo *) NULL;
2346   quantum_scanline = (Quantum *) NULL;
2347   quantum_info = (QuantumInfo *) NULL;
2348
2349   if (setjmp(png_jmpbuf(ping)))
2350     {
2351       /*
2352         PNG image is corrupt.
2353       */
2354       png_destroy_read_struct(&ping,&ping_info,&end_info);
2355
2356       if (pixel_info != (MemoryInfo *) NULL)
2357         pixel_info=RelinquishVirtualMemory(pixel_info);
2358
2359       if (quantum_info != (QuantumInfo *) NULL)
2360         quantum_info=DestroyQuantumInfo(quantum_info);
2361
2362       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2363
2364 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2365       UnlockSemaphoreInfo(ping_semaphore);
2366 #endif
2367
2368       if (logging != MagickFalse)
2369         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2370           "  exit ReadOnePNGImage() with error.");
2371
2372       return(GetFirstImageInList(image));
2373     }
2374
2375   /* {  For navigation to end of SETJMP-protected block.  Within this
2376    *    block, use png_error() instead of Throwing an Exception, to ensure
2377    *    that libpng is able to clean up, and that the semaphore is unlocked.
2378    */
2379
2380 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2381   LockSemaphoreInfo(ping_semaphore);
2382 #endif
2383
2384 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2385   /* Allow benign errors */
2386   png_set_benign_errors(ping, 1);
2387 #endif
2388
2389 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2390   /* Reject images with too many rows or columns */
2391   png_set_user_limits(ping,
2392     (png_uint_32) MagickMin(0x7fffffffL,
2393         GetMagickResourceLimit(WidthResource)),
2394     (png_uint_32) MagickMin(0x7fffffffL,
2395         GetMagickResourceLimit(HeightResource)));
2396 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2397
2398   /*
2399     Prepare PNG for reading.
2400   */
2401
2402   mng_info->image_found++;
2403   png_set_sig_bytes(ping,8);
2404
2405   if (LocaleCompare(image_info->magick,"MNG") == 0)
2406     {
2407 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2408       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2409       png_set_read_fn(ping,image,png_get_data);
2410 #else
2411 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2412       png_permit_empty_plte(ping,MagickTrue);
2413       png_set_read_fn(ping,image,png_get_data);
2414 #else
2415       mng_info->image=image;
2416       mng_info->bytes_in_read_buffer=0;
2417       mng_info->found_empty_plte=MagickFalse;
2418       mng_info->have_saved_bkgd_index=MagickFalse;
2419       png_set_read_fn(ping,mng_info,mng_get_data);
2420 #endif
2421 #endif
2422     }
2423
2424   else
2425     png_set_read_fn(ping,image,png_get_data);
2426
2427   {
2428     const char
2429       *value;
2430
2431     value=GetImageOption(image_info,"png:ignore-crc");
2432     if (value != NULL)
2433     {
2434        /* Turn off CRC checking while reading */
2435        png_set_crc_action(ping, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
2436 #ifdef PNG_IGNORE_ADLER32
2437        /* Turn off ADLER32 checking while reading */
2438        png_set_option(ping, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
2439 #endif
2440      }
2441
2442     ping_preserve_iCCP=MagickTrue;
2443
2444     value=GetImageOption(image_info,"profile:skip");
2445
2446     if (IsOptionMember("ICC",value) == MagickFalse)
2447     {
2448
2449        value=GetImageOption(image_info,"png:preserve-iCCP");
2450
2451        if (value == NULL)
2452           value=GetImageArtifact(image,"png:preserve-iCCP");
2453
2454        if (value != NULL)
2455           ping_preserve_iCCP=MagickTrue;
2456
2457 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2458        /* Don't let libpng check for ICC/sRGB profile because we're going
2459         * to do that anyway.  This feature was added at libpng-1.6.12.
2460         * If logging, go ahead and check and issue a warning as appropriate.
2461         */
2462        if (logging == MagickFalse)
2463           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2464 #endif
2465     }
2466 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2467     else
2468     {
2469        png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2470     }
2471 #endif
2472   }
2473 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2474   /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2475 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2476   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2477 # else
2478   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2479 # endif
2480   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2481   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2482   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2483      (int)sizeof(unused_chunks)/5);
2484   /* Callback for other unknown chunks */
2485   png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2486 #endif
2487
2488 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2489 #  if (PNG_LIBPNG_VER >= 10400)
2490     /* Limit the size of the chunk storage cache used for sPLT, text,
2491      * and unknown chunks.
2492      */
2493     png_set_chunk_cache_max(ping, 32767);
2494 #  endif
2495 #endif
2496
2497 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2498     /* Disable new libpng-1.5.10 feature */
2499     png_set_check_for_invalid_index (ping, 0);
2500 #endif
2501
2502 #if (PNG_LIBPNG_VER < 10400)
2503 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2504    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2505   /* Disable thread-unsafe features of pnggccrd */
2506   if (png_access_version_number() >= 10200)
2507   {
2508     png_uint_32 mmx_disable_mask=0;
2509     png_uint_32 asm_flags;
2510
2511     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2512                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2513                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2514                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2515     asm_flags=png_get_asm_flags(ping);
2516     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2517   }
2518 #  endif
2519 #endif
2520
2521   png_read_info(ping,ping_info);
2522
2523   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2524                &ping_bit_depth,&ping_color_type,
2525                &ping_interlace_method,&ping_compression_method,
2526                &ping_filter_method);
2527
2528   ping_file_depth = ping_bit_depth;
2529
2530   /* Swap bytes if requested */
2531   if (ping_file_depth == 16)
2532   {
2533      const char
2534        *value;
2535
2536      value=GetImageOption(image_info,"png:swap-bytes");
2537
2538      if (value == NULL)
2539         value=GetImageArtifact(image,"png:swap-bytes");
2540
2541      if (value != NULL)
2542         png_set_swap(ping);
2543   }
2544
2545   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2546   {
2547       char
2548         msg[MagickPathExtent];
2549
2550       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2551          (int) ping_color_type);
2552       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2553
2554       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2555          (int) ping_bit_depth);
2556       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2557   }
2558
2559   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2560                       &ping_trans_color);
2561
2562   (void) png_get_bKGD(ping, ping_info, &ping_background);
2563
2564   if (ping_bit_depth < 8)
2565     {
2566        png_set_packing(ping);
2567        ping_bit_depth = 8;
2568     }
2569
2570   image->depth=ping_bit_depth;
2571   image->depth=GetImageQuantumDepth(image,MagickFalse);
2572   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2573
2574   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2575       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2576     {
2577       image->rendering_intent=UndefinedIntent;
2578       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2579       (void) ResetMagickMemory(&image->chromaticity,0,
2580         sizeof(image->chromaticity));
2581     }
2582
2583   if (logging != MagickFalse)
2584     {
2585       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2586         "    PNG width: %.20g, height: %.20g\n"
2587         "    PNG color_type: %d, bit_depth: %d\n"
2588         "    PNG compression_method: %d\n"
2589         "    PNG interlace_method: %d, filter_method: %d",
2590         (double) ping_width, (double) ping_height,
2591         ping_color_type, ping_bit_depth,
2592         ping_compression_method,
2593         ping_interlace_method,ping_filter_method);
2594
2595     }
2596
2597   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2598     {
2599       ping_found_iCCP=MagickTrue;
2600       if (logging != MagickFalse)
2601         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2602           "    Found PNG iCCP chunk.");
2603     }
2604
2605   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2606     {
2607       ping_found_gAMA=MagickTrue;
2608       if (logging != MagickFalse)
2609         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2610           "    Found PNG gAMA chunk.");
2611     }
2612
2613   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2614     {
2615       ping_found_cHRM=MagickTrue;
2616       if (logging != MagickFalse)
2617         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2618           "    Found PNG cHRM chunk.");
2619     }
2620
2621   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2622       PNG_INFO_sRGB))
2623     {
2624       ping_found_sRGB=MagickTrue;
2625       if (logging != MagickFalse)
2626         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2627           "    Found PNG sRGB chunk.");
2628     }
2629
2630 #ifdef PNG_READ_iCCP_SUPPORTED
2631     if (ping_found_iCCP !=MagickTrue &&
2632       ping_found_sRGB != MagickTrue &&
2633       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2634     {
2635       ping_found_iCCP=MagickTrue;
2636       if (logging != MagickFalse)
2637         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2638           "    Found PNG iCCP chunk.");
2639     }
2640
2641   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2642     {
2643       int
2644         compression;
2645
2646 #if (PNG_LIBPNG_VER < 10500)
2647       png_charp
2648         info;
2649 #else
2650       png_bytep
2651         info;
2652 #endif
2653
2654       png_charp
2655         name;
2656
2657       png_uint_32
2658         profile_length;
2659
2660       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2661         &profile_length);
2662
2663       if (profile_length != 0)
2664         {
2665           StringInfo
2666             *profile;
2667
2668           if (logging != MagickFalse)
2669             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2670               "    Reading PNG iCCP chunk.");
2671
2672           profile=BlobToStringInfo(info,profile_length);
2673
2674           if (profile == (StringInfo *) NULL)
2675           {
2676             png_warning(ping, "ICC profile is NULL");
2677             profile=DestroyStringInfo(profile);
2678           }
2679           else
2680           {
2681             if (ping_preserve_iCCP == MagickFalse)
2682             {
2683                  int
2684                    icheck,
2685                    got_crc=0;
2686
2687
2688                  png_uint_32
2689                    length,
2690                    profile_crc=0;
2691
2692                  unsigned char
2693                    *data;
2694
2695                  length=(png_uint_32) GetStringInfoLength(profile);
2696
2697                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2698                  {
2699                    if (length == sRGB_info[icheck].len)
2700                    {
2701                      if (got_crc == 0)
2702                      {
2703                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2704                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2705                          (unsigned long) length);
2706
2707                        data=GetStringInfoDatum(profile);
2708                        profile_crc=crc32(0,data,length);
2709
2710                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2711                            "      with crc=%8x",(unsigned int) profile_crc);
2712                        got_crc++;
2713                      }
2714
2715                      if (profile_crc == sRGB_info[icheck].crc)
2716                      {
2717                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2718                             "      It is sRGB with rendering intent = %s",
2719                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2720                              sRGB_info[icheck].intent));
2721                         if (image->rendering_intent==UndefinedIntent)
2722                         {
2723                           image->rendering_intent=
2724                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2725                              sRGB_info[icheck].intent);
2726                         }
2727                         break;
2728                      }
2729                    }
2730                  }
2731                  if (sRGB_info[icheck].len == 0)
2732                  {
2733                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2734                         "    Got %lu-byte ICC profile not recognized as sRGB",
2735                         (unsigned long) length);
2736                     (void) SetImageProfile(image,"icc",profile,exception);
2737                  }
2738             }
2739             else /* Preserve-iCCP */
2740             {
2741                     (void) SetImageProfile(image,"icc",profile,exception);
2742             }
2743
2744             profile=DestroyStringInfo(profile);
2745           }
2746       }
2747     }
2748 #endif
2749
2750 #if defined(PNG_READ_sRGB_SUPPORTED)
2751   {
2752     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2753         PNG_INFO_sRGB))
2754     {
2755       if (png_get_sRGB(ping,ping_info,&intent))
2756       {
2757         if (image->rendering_intent == UndefinedIntent)
2758           image->rendering_intent=
2759              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2760
2761         if (logging != MagickFalse)
2762           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2763             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2764       }
2765     }
2766
2767     else if (mng_info->have_global_srgb)
2768       {
2769         if (image->rendering_intent == UndefinedIntent)
2770           image->rendering_intent=
2771             Magick_RenderingIntent_from_PNG_RenderingIntent
2772             (mng_info->global_srgb_intent);
2773       }
2774   }
2775 #endif
2776
2777
2778   {
2779      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2780        if (mng_info->have_global_gama)
2781          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2782
2783      if (png_get_gAMA(ping,ping_info,&file_gamma))
2784        {
2785          image->gamma=(float) file_gamma;
2786          if (logging != MagickFalse)
2787            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2788              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2789        }
2790   }
2791
2792   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2793     {
2794       if (mng_info->have_global_chrm != MagickFalse)
2795         {
2796           (void) png_set_cHRM(ping,ping_info,
2797             mng_info->global_chrm.white_point.x,
2798             mng_info->global_chrm.white_point.y,
2799             mng_info->global_chrm.red_primary.x,
2800             mng_info->global_chrm.red_primary.y,
2801             mng_info->global_chrm.green_primary.x,
2802             mng_info->global_chrm.green_primary.y,
2803             mng_info->global_chrm.blue_primary.x,
2804             mng_info->global_chrm.blue_primary.y);
2805         }
2806     }
2807
2808   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2809     {
2810       (void) png_get_cHRM(ping,ping_info,
2811         &image->chromaticity.white_point.x,
2812         &image->chromaticity.white_point.y,
2813         &image->chromaticity.red_primary.x,
2814         &image->chromaticity.red_primary.y,
2815         &image->chromaticity.green_primary.x,
2816         &image->chromaticity.green_primary.y,
2817         &image->chromaticity.blue_primary.x,
2818         &image->chromaticity.blue_primary.y);
2819
2820        ping_found_cHRM=MagickTrue;
2821
2822        if (image->chromaticity.red_primary.x>0.6399f &&
2823            image->chromaticity.red_primary.x<0.6401f &&
2824            image->chromaticity.red_primary.y>0.3299f &&
2825            image->chromaticity.red_primary.y<0.3301f &&
2826            image->chromaticity.green_primary.x>0.2999f &&
2827            image->chromaticity.green_primary.x<0.3001f &&
2828            image->chromaticity.green_primary.y>0.5999f &&
2829            image->chromaticity.green_primary.y<0.6001f &&
2830            image->chromaticity.blue_primary.x>0.1499f &&
2831            image->chromaticity.blue_primary.x<0.1501f &&
2832            image->chromaticity.blue_primary.y>0.0599f &&
2833            image->chromaticity.blue_primary.y<0.0601f &&
2834            image->chromaticity.white_point.x>0.3126f &&
2835            image->chromaticity.white_point.x<0.3128f &&
2836            image->chromaticity.white_point.y>0.3289f &&
2837            image->chromaticity.white_point.y<0.3291f)
2838           ping_found_sRGB_cHRM=MagickTrue;
2839     }
2840
2841   if (image->rendering_intent != UndefinedIntent)
2842     {
2843       if (ping_found_sRGB != MagickTrue &&
2844           (ping_found_gAMA != MagickTrue ||
2845           (image->gamma > .45 && image->gamma < .46)) &&
2846           (ping_found_cHRM != MagickTrue ||
2847           ping_found_sRGB_cHRM != MagickFalse) &&
2848           ping_found_iCCP != MagickTrue)
2849       {
2850          png_set_sRGB(ping,ping_info,
2851             Magick_RenderingIntent_to_PNG_RenderingIntent
2852             (image->rendering_intent));
2853          file_gamma=1.000f/2.200f;
2854          ping_found_sRGB=MagickTrue;
2855          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2856            "    Setting sRGB as if in input");
2857       }
2858     }
2859
2860 #if defined(PNG_oFFs_SUPPORTED)
2861   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2862     {
2863       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2864       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2865
2866       if (logging != MagickFalse)
2867         if (image->page.x || image->page.y)
2868           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2869             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2870             image->page.x,(double) image->page.y);
2871     }
2872 #endif
2873 #if defined(PNG_pHYs_SUPPORTED)
2874   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2875     {
2876       if (mng_info->have_global_phys)
2877         {
2878           png_set_pHYs(ping,ping_info,
2879                        mng_info->global_x_pixels_per_unit,
2880                        mng_info->global_y_pixels_per_unit,
2881                        mng_info->global_phys_unit_type);
2882         }
2883     }
2884
2885   x_resolution=0;
2886   y_resolution=0;
2887   unit_type=0;
2888   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2889     {
2890       /*
2891         Set image resolution.
2892       */
2893       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2894         &unit_type);
2895       image->resolution.x=(double) x_resolution;
2896       image->resolution.y=(double) y_resolution;
2897
2898       if (unit_type == PNG_RESOLUTION_METER)
2899         {
2900           image->units=PixelsPerCentimeterResolution;
2901           image->resolution.x=(double) x_resolution/100.0;
2902           image->resolution.y=(double) y_resolution/100.0;
2903         }
2904
2905       if (logging != MagickFalse)
2906         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2907           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2908           (double) x_resolution,(double) y_resolution,unit_type);
2909     }
2910 #endif
2911
2912   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2913     {
2914       png_colorp
2915         palette;
2916
2917       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2918
2919       if ((number_colors == 0) &&
2920           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2921         {
2922           if (mng_info->global_plte_length)
2923             {
2924               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2925                 (int) mng_info->global_plte_length);
2926
2927               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2928               {
2929                 if (mng_info->global_trns_length)
2930                   {
2931                     png_warning(ping,
2932                       "global tRNS has more entries than global PLTE");
2933                   }
2934                 else
2935                   {
2936                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2937                        (int) mng_info->global_trns_length,NULL);
2938                   }
2939                }
2940 #ifdef PNG_READ_bKGD_SUPPORTED
2941               if (
2942 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2943                    mng_info->have_saved_bkgd_index ||
2944 #endif
2945                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2946                     {
2947                       png_color_16
2948                          background;
2949
2950 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2951                       if (mng_info->have_saved_bkgd_index)
2952                         background.index=mng_info->saved_bkgd_index;
2953 #endif
2954                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2955                         background.index=ping_background->index;
2956
2957                       background.red=(png_uint_16)
2958                         mng_info->global_plte[background.index].red;
2959
2960                       background.green=(png_uint_16)
2961                         mng_info->global_plte[background.index].green;
2962
2963                       background.blue=(png_uint_16)
2964                         mng_info->global_plte[background.index].blue;
2965
2966                       background.gray=(png_uint_16)
2967                         mng_info->global_plte[background.index].green;
2968
2969                       png_set_bKGD(ping,ping_info,&background);
2970                     }
2971 #endif
2972                 }
2973               else
2974                 png_error(ping,"No global PLTE in file");
2975             }
2976         }
2977
2978 #ifdef PNG_READ_bKGD_SUPPORTED
2979   if (mng_info->have_global_bkgd &&
2980           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2981       image->background_color=mng_info->mng_global_bkgd;
2982
2983   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2984     {
2985       unsigned int
2986         bkgd_scale;
2987
2988       /* Set image background color.
2989        * Scale background components to 16-bit, then scale
2990        * to quantum depth
2991        */
2992
2993         bkgd_scale = 1;
2994
2995         if (ping_file_depth == 1)
2996            bkgd_scale = 255;
2997
2998         else if (ping_file_depth == 2)
2999            bkgd_scale = 85;
3000
3001         else if (ping_file_depth == 4)
3002            bkgd_scale = 17;
3003
3004         if (ping_file_depth <= 8)
3005            bkgd_scale *= 257;
3006
3007         ping_background->red *= bkgd_scale;
3008         ping_background->green *= bkgd_scale;
3009         ping_background->blue *= bkgd_scale;
3010
3011         if (logging != MagickFalse)
3012           {
3013             if (logging != MagickFalse)
3014               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3015                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
3016                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d)",
3017                  ping_background->red,ping_background->green,
3018                  ping_background->blue,
3019                  bkgd_scale,ping_background->red,
3020                  ping_background->green,ping_background->blue);
3021           }
3022
3023         image->background_color.red=
3024             ScaleShortToQuantum(ping_background->red);
3025
3026         image->background_color.green=
3027             ScaleShortToQuantum(ping_background->green);
3028
3029         image->background_color.blue=
3030           ScaleShortToQuantum(ping_background->blue);
3031
3032         image->background_color.alpha=OpaqueAlpha;
3033
3034         if (logging != MagickFalse)
3035           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3036             "    image->background_color=(%.20g,%.20g,%.20g).",
3037             (double) image->background_color.red,
3038             (double) image->background_color.green,
3039             (double) image->background_color.blue);
3040     }
3041 #endif /* PNG_READ_bKGD_SUPPORTED */
3042
3043   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3044     {
3045       /*
3046         Image has a tRNS chunk.
3047       */
3048       int
3049         max_sample;
3050
3051       size_t
3052         one = 1;
3053
3054       if (logging != MagickFalse)
3055         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3056           "    Reading PNG tRNS chunk.");
3057
3058       max_sample = (int) ((one << ping_file_depth) - 1);
3059
3060       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
3061           (int)ping_trans_color->gray > max_sample) ||
3062           (ping_color_type == PNG_COLOR_TYPE_RGB &&
3063           ((int)ping_trans_color->red > max_sample ||
3064           (int)ping_trans_color->green > max_sample ||
3065           (int)ping_trans_color->blue > max_sample)))
3066         {
3067           if (logging != MagickFalse)
3068             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3069               "    Ignoring PNG tRNS chunk with out-of-range sample.");
3070           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
3071           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
3072           image->alpha_trait=UndefinedPixelTrait;
3073         }
3074       else
3075         {
3076           int
3077             scale_to_short;
3078
3079           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
3080
3081           /* Scale transparent_color to short */
3082           transparent_color.red= scale_to_short*ping_trans_color->red;
3083           transparent_color.green= scale_to_short*ping_trans_color->green;
3084           transparent_color.blue= scale_to_short*ping_trans_color->blue;
3085           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
3086
3087           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3088             {
3089               if (logging != MagickFalse)
3090               {
3091                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3092                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3093                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3094
3095               }
3096               transparent_color.red=transparent_color.alpha;
3097               transparent_color.green=transparent_color.alpha;
3098               transparent_color.blue=transparent_color.alpha;
3099             }
3100         }
3101     }
3102 #if defined(PNG_READ_sBIT_SUPPORTED)
3103   if (mng_info->have_global_sbit)
3104     {
3105       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3106         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3107     }
3108 #endif
3109   num_passes=png_set_interlace_handling(ping);
3110
3111   png_read_update_info(ping,ping_info);
3112
3113   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3114
3115   /*
3116     Initialize image structure.
3117   */
3118   mng_info->image_box.left=0;
3119   mng_info->image_box.right=(ssize_t) ping_width;
3120   mng_info->image_box.top=0;
3121   mng_info->image_box.bottom=(ssize_t) ping_height;
3122   if (mng_info->mng_type == 0)
3123     {
3124       mng_info->mng_width=ping_width;
3125       mng_info->mng_height=ping_height;
3126       mng_info->frame=mng_info->image_box;
3127       mng_info->clip=mng_info->image_box;
3128     }
3129
3130   else
3131     {
3132       image->page.y=mng_info->y_off[mng_info->object_id];
3133     }
3134
3135   image->compression=ZipCompression;
3136   image->columns=ping_width;
3137   image->rows=ping_height;
3138
3139   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3140       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3141     {
3142       double
3143         image_gamma = image->gamma;
3144
3145       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3146          "    image->gamma=%f",(float) image_gamma);
3147
3148       if (image_gamma > 0.75)
3149         {
3150           /* Set image->rendering_intent to Undefined,
3151            * image->colorspace to GRAY, and reset image->chromaticity.
3152            */
3153           image->intensity = Rec709LuminancePixelIntensityMethod;
3154           SetImageColorspace(image,GRAYColorspace,exception);
3155         }
3156       else
3157         {
3158           RenderingIntent
3159             save_rendering_intent = image->rendering_intent;
3160           ChromaticityInfo
3161             save_chromaticity = image->chromaticity;
3162
3163           SetImageColorspace(image,GRAYColorspace,exception);
3164           image->rendering_intent = save_rendering_intent;
3165           image->chromaticity = save_chromaticity;
3166         }
3167
3168       image->gamma = image_gamma;
3169     }
3170   else
3171     {
3172       double
3173         image_gamma = image->gamma;
3174
3175       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3176          "    image->gamma=%f",(float) image_gamma);
3177
3178       if (image_gamma > 0.75)
3179         {
3180           /* Set image->rendering_intent to Undefined,
3181            * image->colorspace to GRAY, and reset image->chromaticity.
3182            */
3183           image->intensity = Rec709LuminancePixelIntensityMethod;
3184           SetImageColorspace(image,RGBColorspace,exception);
3185         }
3186       else
3187         {
3188           RenderingIntent
3189             save_rendering_intent = image->rendering_intent;
3190           ChromaticityInfo
3191             save_chromaticity = image->chromaticity;
3192
3193           SetImageColorspace(image,sRGBColorspace,exception);
3194           image->rendering_intent = save_rendering_intent;
3195           image->chromaticity = save_chromaticity;
3196         }
3197
3198       image->gamma = image_gamma;
3199     }
3200
3201   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3202       "    image->colorspace=%d",(int) image->colorspace);
3203
3204   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3205       ((int) ping_bit_depth < 16 &&
3206       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3207     {
3208       size_t
3209         one;
3210
3211       image->storage_class=PseudoClass;
3212       one=1;
3213       image->colors=one << ping_file_depth;
3214 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3215       if (image->colors > 256)
3216         image->colors=256;
3217 #else
3218       if (image->colors > 65536L)
3219         image->colors=65536L;
3220 #endif
3221       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3222         {
3223           png_colorp
3224             palette;
3225
3226           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3227           image->colors=(size_t) number_colors;
3228
3229           if (logging != MagickFalse)
3230             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3231               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3232         }
3233     }
3234
3235   if (image->storage_class == PseudoClass)
3236     {
3237       /*
3238         Initialize image colormap.
3239       */
3240       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3241         png_error(ping,"Memory allocation failed");
3242
3243       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3244         {
3245           png_colorp
3246             palette;
3247
3248           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3249
3250           for (i=0; i < (ssize_t) number_colors; i++)
3251           {
3252             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3253             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3254             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3255           }
3256
3257           for ( ; i < (ssize_t) image->colors; i++)
3258           {
3259             image->colormap[i].red=0;
3260             image->colormap[i].green=0;
3261             image->colormap[i].blue=0;
3262           }
3263         }
3264
3265       else
3266         {
3267           Quantum
3268             scale;
3269
3270           scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3271
3272 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3273           scale = ScaleShortToQuantum(scale);
3274 #endif
3275
3276           for (i=0; i < (ssize_t) image->colors; i++)
3277           {
3278             image->colormap[i].red=(Quantum) (i*scale);
3279             image->colormap[i].green=(Quantum) (i*scale);
3280             image->colormap[i].blue=(Quantum) (i*scale);
3281           }
3282        }
3283     }
3284
3285    /* Set some properties for reporting by "identify" */
3286     {
3287       char
3288         msg[MagickPathExtent];
3289
3290      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3291         ping_interlace_method in value */
3292
3293      (void) FormatLocaleString(msg,MagickPathExtent,
3294          "%d, %d",(int) ping_width, (int) ping_height);
3295      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3296
3297      (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3298         (int) ping_file_depth);
3299      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3300
3301      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3302          (int) ping_color_type,
3303          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3304      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3305
3306      if (ping_interlace_method == 0)
3307        {
3308          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3309             (int) ping_interlace_method);
3310        }
3311      else if (ping_interlace_method == 1)
3312        {
3313          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3314             (int) ping_interlace_method);
3315        }
3316      else
3317        {
3318          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3319             (int) ping_interlace_method);
3320        }
3321        (void) SetImageProperty(image,"png:IHDR.interlace_method",
3322          msg,exception);
3323
3324      if (number_colors != 0)
3325        {
3326          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3327             (int) number_colors);
3328          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3329             exception);
3330        }
3331    }
3332 #if defined(PNG_tIME_SUPPORTED)
3333    read_tIME_chunk(image,ping,ping_info,exception);
3334 #endif
3335
3336
3337   /*
3338     Read image scanlines.
3339   */
3340   if (image->delay != 0)
3341     mng_info->scenes_found++;
3342
3343   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3344       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3345       (image_info->first_scene+image_info->number_scenes))))
3346     {
3347       /* This happens later in non-ping decodes */
3348       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3349         image->storage_class=DirectClass;
3350       image->alpha_trait=
3351         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3352          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3353          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3354         BlendPixelTrait : UndefinedPixelTrait;
3355
3356       if (logging != MagickFalse)
3357         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3358           "    Skipping PNG image data for scene %.20g",(double)
3359           mng_info->scenes_found-1);
3360       png_destroy_read_struct(&ping,&ping_info,&end_info);
3361
3362 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3363       UnlockSemaphoreInfo(ping_semaphore);
3364 #endif
3365
3366       if (logging != MagickFalse)
3367         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3368           "  exit ReadOnePNGImage().");
3369
3370       return(image);
3371     }
3372
3373   if (logging != MagickFalse)
3374     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3375       "    Reading PNG IDAT chunk(s)");
3376
3377   status=SetImageExtent(image,image->columns,image->rows,exception);
3378   if (status == MagickFalse)
3379     {
3380       png_destroy_read_struct(&ping,&ping_info,&end_info);
3381 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3382       UnlockSemaphoreInfo(ping_semaphore);
3383 #endif
3384       return(DestroyImageList(image));
3385     }
3386
3387   if (num_passes > 1)
3388     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3389       sizeof(*ping_pixels));
3390   else
3391     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3392
3393   if (pixel_info == (MemoryInfo *) NULL)
3394     png_error(ping,"Memory allocation failed");
3395   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3396
3397   if (logging != MagickFalse)
3398     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3399       "    Converting PNG pixels to pixel packets");
3400   /*
3401     Convert PNG pixels to pixel packets.
3402   */
3403   quantum_info=AcquireQuantumInfo(image_info,image);
3404
3405   if (quantum_info == (QuantumInfo *) NULL)
3406      png_error(ping,"Failed to allocate quantum_info");
3407
3408   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3409
3410   {
3411
3412    MagickBooleanType
3413      found_transparent_pixel;
3414
3415   found_transparent_pixel=MagickFalse;
3416
3417   if (image->storage_class == DirectClass)
3418     {
3419       for (pass=0; pass < num_passes; pass++)
3420       {
3421         /*
3422           Convert image to DirectClass pixel packets.
3423         */
3424         image->alpha_trait=
3425             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3426             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3427             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3428             BlendPixelTrait : UndefinedPixelTrait;
3429
3430         for (y=0; y < (ssize_t) image->rows; y++)
3431         {
3432           if (num_passes > 1)
3433             row_offset=ping_rowbytes*y;
3434
3435           else
3436             row_offset=0;
3437
3438           png_read_row(ping,ping_pixels+row_offset,NULL);
3439
3440           if (pass < num_passes-1)
3441             continue;
3442
3443           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3444
3445           if (q == (Quantum *) NULL)
3446             break;
3447
3448           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3449             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3450               GrayQuantum,ping_pixels+row_offset,exception);
3451
3452           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3453             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3454               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3455
3456           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3457             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3458               RGBAQuantum,ping_pixels+row_offset,exception);
3459
3460           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3461             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3462               IndexQuantum,ping_pixels+row_offset,exception);
3463
3464           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3465             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3466               RGBQuantum,ping_pixels+row_offset,exception);
3467
3468           if (found_transparent_pixel == MagickFalse)
3469             {
3470               /* Is there a transparent pixel in the row? */
3471               if (y== 0 && logging != MagickFalse)
3472                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3473                    "    Looking for cheap transparent pixel");
3474
3475               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3476               {
3477                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3478                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3479                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3480                   {
3481                     if (logging != MagickFalse)
3482                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3483                         "    ...got one.");
3484
3485                     found_transparent_pixel = MagickTrue;
3486                     break;
3487                   }
3488                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3489                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3490                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3491                     transparent_color.red &&
3492                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3493                     transparent_color.green &&
3494                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3495                     transparent_color.blue))
3496                   {
3497                     if (logging != MagickFalse)
3498                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3499                         "    ...got one.");
3500                     found_transparent_pixel = MagickTrue;
3501                     break;
3502                   }
3503                 q+=GetPixelChannels(image);
3504               }
3505             }
3506
3507           if (num_passes == 1)
3508             {
3509               status=SetImageProgress(image,LoadImageTag,
3510                   (MagickOffsetType) y, image->rows);
3511
3512               if (status == MagickFalse)
3513                 break;
3514             }
3515           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3516             break;
3517         }
3518
3519         if (num_passes != 1)
3520           {
3521             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3522             if (status == MagickFalse)
3523               break;
3524           }
3525       }
3526     }
3527
3528   else /* image->storage_class != DirectClass */
3529
3530     for (pass=0; pass < num_passes; pass++)
3531     {
3532       register Quantum
3533         *r;
3534
3535       /*
3536         Convert grayscale image to PseudoClass pixel packets.
3537       */
3538       if (logging != MagickFalse)
3539         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3540           "    Converting grayscale pixels to pixel packets");
3541
3542       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3543         BlendPixelTrait : UndefinedPixelTrait;
3544
3545       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3546         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3547         sizeof(*quantum_scanline));
3548
3549       if (quantum_scanline == (Quantum *) NULL)
3550         png_error(ping,"Memory allocation failed");
3551
3552       for (y=0; y < (ssize_t) image->rows; y++)
3553       {
3554         Quantum
3555            alpha;
3556
3557         if (num_passes > 1)
3558           row_offset=ping_rowbytes*y;
3559
3560         else
3561           row_offset=0;
3562
3563         png_read_row(ping,ping_pixels+row_offset,NULL);
3564
3565         if (pass < num_passes-1)
3566           continue;
3567
3568         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3569
3570         if (q == (Quantum *) NULL)
3571           break;
3572
3573         p=ping_pixels+row_offset;
3574         r=quantum_scanline;
3575
3576         switch (ping_bit_depth)
3577         {
3578           case 8:
3579           {
3580
3581             if (ping_color_type == 4)
3582               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3583               {
3584                 *r++=*p++;
3585
3586                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3587
3588                 SetPixelAlpha(image,alpha,q);
3589
3590                 if (alpha != OpaqueAlpha)
3591                   found_transparent_pixel = MagickTrue;
3592
3593                 q+=GetPixelChannels(image);
3594               }
3595
3596             else
3597               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3598                 *r++=*p++;
3599
3600             break;
3601           }
3602
3603           case 16:
3604           {
3605             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3606             {
3607 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3608               unsigned short
3609                 quantum;
3610
3611               if (image->colors > 256)
3612                 quantum=(((unsigned int) *p++) << 8);
3613
3614               else
3615                 quantum=0;
3616
3617               quantum|=(*p++);
3618               *r=ScaleShortToQuantum(quantum);
3619               r++;
3620
3621               if (ping_color_type == 4)
3622                 {
3623                   if (image->colors > 256)
3624                     quantum=(((unsigned int) *p++) << 8);
3625                   else
3626                     quantum=0;
3627
3628                   quantum|=(*p++);
3629
3630                   alpha=ScaleShortToQuantum(quantum);
3631                   SetPixelAlpha(image,alpha,q);
3632
3633                   if (alpha != OpaqueAlpha)
3634                     found_transparent_pixel = MagickTrue;
3635
3636                   q+=GetPixelChannels(image);
3637                 }
3638
3639 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3640               *r++=(*p++);
3641               p++; /* strip low byte */
3642
3643               if (ping_color_type == 4)
3644                 {
3645                   SetPixelAlpha(image,*p++,q);
3646
3647                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3648                     found_transparent_pixel = MagickTrue;
3649
3650                   p++;
3651                   q+=GetPixelChannels(image);
3652                 }
3653 #endif
3654             }
3655
3656             break;
3657           }
3658
3659           default:
3660             break;
3661         }
3662
3663         /*
3664           Transfer image scanline.
3665         */
3666         r=quantum_scanline;
3667
3668         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3669
3670         if (q == (Quantum *) NULL)
3671           break;
3672         for (x=0; x < (ssize_t) image->columns; x++)
3673         {
3674           SetPixelIndex(image,*r++,q);
3675           q+=GetPixelChannels(image);
3676         }
3677
3678         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3679           break;
3680
3681         if (num_passes == 1)
3682           {
3683             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3684               image->rows);
3685
3686             if (status == MagickFalse)
3687               break;
3688           }
3689       }
3690
3691       if (num_passes != 1)
3692         {
3693           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3694
3695           if (status == MagickFalse)
3696             break;
3697         }
3698
3699       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3700     }
3701
3702     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3703       UndefinedPixelTrait;
3704
3705     if (logging != MagickFalse)
3706       {
3707         if (found_transparent_pixel != MagickFalse)
3708           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3709             "    Found transparent pixel");
3710         else
3711           {
3712             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3713               "    No transparent pixel was found");
3714
3715             ping_color_type&=0x03;
3716           }
3717       }
3718     }
3719
3720   quantum_info=DestroyQuantumInfo(quantum_info);
3721
3722   if (image->storage_class == PseudoClass)
3723     {
3724       PixelTrait
3725         alpha_trait;
3726
3727       alpha_trait=image->alpha_trait;
3728       image->alpha_trait=UndefinedPixelTrait;
3729       (void) SyncImage(image,exception);
3730       image->alpha_trait=alpha_trait;
3731     }
3732
3733   png_read_end(ping,end_info);
3734
3735   if (logging != MagickFalse)
3736   {
3737     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3738        "  image->storage_class=%d\n",(int) image->storage_class);
3739   }
3740
3741   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3742       (ssize_t) image_info->first_scene && image->delay != 0)
3743     {
3744       png_destroy_read_struct(&ping,&ping_info,&end_info);
3745       pixel_info=RelinquishVirtualMemory(pixel_info);
3746       image->colors=2;
3747       (void) SetImageBackgroundColor(image,exception);
3748 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3749       UnlockSemaphoreInfo(ping_semaphore);
3750 #endif
3751       if (logging != MagickFalse)
3752         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3753           "  exit ReadOnePNGImage() early.");
3754       return(image);
3755     }
3756
3757   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3758     {
3759       ClassType
3760         storage_class;
3761
3762       /*
3763         Image has a transparent background.
3764       */
3765       storage_class=image->storage_class;
3766       image->alpha_trait=BlendPixelTrait;
3767
3768 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3769
3770       if (storage_class == PseudoClass)
3771         {
3772           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3773             {
3774               for (x=0; x < ping_num_trans; x++)
3775               {
3776                  image->colormap[x].alpha_trait=BlendPixelTrait;
3777                  image->colormap[x].alpha =
3778                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3779               }
3780             }
3781
3782           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3783             {
3784               for (x=0; x < (int) image->colors; x++)
3785               {
3786                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3787                      transparent_color.alpha)
3788                  {
3789                     image->colormap[x].alpha_trait=BlendPixelTrait;
3790                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3791                  }
3792               }
3793             }
3794           (void) SyncImage(image,exception);
3795         }
3796
3797 #if 1 /* Should have already been done above, but glennrp problem P10
3798        * needs this.
3799        */
3800       else
3801         {
3802           for (y=0; y < (ssize_t) image->rows; y++)
3803           {
3804             image->storage_class=storage_class;
3805             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3806
3807             if (q == (Quantum *) NULL)
3808               break;
3809
3810
3811             /* Caution: on a Q8 build, this does not distinguish between
3812              * 16-bit colors that differ only in the low byte
3813              */
3814             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3815             {
3816               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3817                   transparent_color.red &&
3818                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3819                   transparent_color.green &&
3820                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3821                   transparent_color.blue)
3822                 {
3823                   SetPixelAlpha(image,TransparentAlpha,q);
3824                 }
3825
3826 #if 0 /* I have not found a case where this is needed. */
3827               else
3828                 {
3829                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3830                 }
3831 #endif
3832
3833               q+=GetPixelChannels(image);
3834             }
3835
3836             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3837                break;
3838           }
3839         }
3840 #endif
3841
3842       image->storage_class=DirectClass;
3843     }
3844
3845   for (j = 0; j < 2; j++)
3846   {
3847     if (j == 0)
3848       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3849           MagickTrue : MagickFalse;
3850     else
3851       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3852           MagickTrue : MagickFalse;
3853
3854     if (status != MagickFalse)
3855       for (i=0; i < (ssize_t) num_text; i++)
3856       {
3857         /* Check for a profile */
3858
3859         if (logging != MagickFalse)
3860           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3861             "    Reading PNG text chunk");
3862
3863         if (strlen(text[i].key) > 16 &&
3864             memcmp(text[i].key, "Raw profile type ",17) == 0)
3865           {
3866             const char
3867               *value;
3868
3869             value=GetImageOption(image_info,"profile:skip");
3870
3871             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3872             {
3873                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3874                   (int) i,exception);
3875                num_raw_profiles++;
3876                if (logging != MagickFalse)
3877                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3878                    "    Read raw profile %s",text[i].key+17);
3879             }
3880             else
3881             {
3882                if (logging != MagickFalse)
3883                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3884                    "    Skipping raw profile %s",text[i].key+17);
3885             }
3886           }
3887
3888         else
3889           {
3890             char
3891               *value;
3892
3893             length=text[i].text_length;
3894             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3895               sizeof(*value));
3896             if (value == (char *) NULL)
3897               {
3898                 png_error(ping,"Memory allocation failed");
3899                 break;
3900               }
3901             *value='\0';
3902             (void) ConcatenateMagickString(value,text[i].text,length+2);
3903
3904             /* Don't save "density" or "units" property if we have a pHYs
3905              * chunk
3906              */
3907             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3908                 (LocaleCompare(text[i].key,"density") != 0 &&
3909                 LocaleCompare(text[i].key,"units") != 0))
3910                (void) SetImageProperty(image,text[i].key,value,exception);
3911
3912             if (logging != MagickFalse)
3913             {
3914               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3915                 "      length: %lu\n"
3916                 "      Keyword: %s",
3917                 (unsigned long) length,
3918                 text[i].key);
3919             }
3920
3921             value=DestroyString(value);
3922           }
3923       }
3924     num_text_total += num_text;
3925   }
3926
3927 #ifdef MNG_OBJECT_BUFFERS
3928   /*
3929     Store the object if necessary.
3930   */
3931   if (object_id && !mng_info->frozen[object_id])
3932     {
3933       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3934         {
3935           /*
3936             create a new object buffer.
3937           */
3938           mng_info->ob[object_id]=(MngBuffer *)
3939             AcquireMagickMemory(sizeof(MngBuffer));
3940
3941           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3942             {
3943               mng_info->ob[object_id]->image=(Image *) NULL;
3944               mng_info->ob[object_id]->reference_count=1;
3945             }
3946         }
3947
3948       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3949           mng_info->ob[object_id]->frozen)
3950         {
3951           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3952              png_error(ping,"Memory allocation failed");
3953
3954           if (mng_info->ob[object_id]->frozen)
3955             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3956         }
3957
3958       else
3959         {
3960
3961           if (mng_info->ob[object_id]->image != (Image *) NULL)
3962             mng_info->ob[object_id]->image=DestroyImage
3963                 (mng_info->ob[object_id]->image);
3964
3965           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3966             exception);
3967
3968           if (mng_info->ob[object_id]->image != (Image *) NULL)
3969             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3970
3971           else
3972             png_error(ping, "Cloning image for object buffer failed");
3973
3974           if (ping_width > 250000L || ping_height > 250000L)
3975              png_error(ping,"PNG Image dimensions are too large.");
3976
3977           mng_info->ob[object_id]->width=ping_width;
3978           mng_info->ob[object_id]->height=ping_height;
3979           mng_info->ob[object_id]->color_type=ping_color_type;
3980           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3981           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3982           mng_info->ob[object_id]->compression_method=
3983              ping_compression_method;
3984           mng_info->ob[object_id]->filter_method=ping_filter_method;
3985
3986           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3987             {
3988               png_colorp
3989                 plte;
3990
3991               /*
3992                 Copy the PLTE to the object buffer.
3993               */
3994               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3995               mng_info->ob[object_id]->plte_length=number_colors;
3996
3997               for (i=0; i < number_colors; i++)
3998               {
3999                 mng_info->ob[object_id]->plte[i]=plte[i];
4000               }
4001             }
4002
4003           else
4004               mng_info->ob[object_id]->plte_length=0;
4005         }
4006     }
4007 #endif
4008
4009    /* Set image->alpha_trait to MagickTrue if the input colortype supports
4010     * alpha or if a valid tRNS chunk is present, no matter whether there
4011     * is actual transparency present.
4012     */
4013     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
4014         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
4015         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
4016         BlendPixelTrait : UndefinedPixelTrait;
4017
4018 #if 0  /* I'm not sure what's wrong here but it does not work. */
4019     if (image->alpha_trait != UndefinedPixelTrait)
4020     {
4021       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
4022         (void) SetImageType(image,GrayscaleAlphaType,exception);
4023
4024       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4025         (void) SetImageType(image,PaletteAlphaType,exception);
4026
4027       else
4028         (void) SetImageType(image,TrueColorAlphaType,exception);
4029     }
4030
4031     else
4032     {
4033       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
4034         (void) SetImageType(image,GrayscaleType,exception);
4035
4036       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
4037         (void) SetImageType(image,PaletteType,exception);
4038
4039       else
4040         (void) SetImageType(image,TrueColorType,exception);
4041     }
4042 #endif
4043
4044    /* Set more properties for identify to retrieve */
4045    {
4046      char
4047        msg[MagickPathExtent];
4048
4049      if (num_text_total != 0)
4050        {
4051          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
4052          (void) FormatLocaleString(msg,MagickPathExtent,
4053             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
4054          (void) SetImageProperty(image,"png:text",msg,
4055                 exception);
4056        }
4057
4058      if (num_raw_profiles != 0)
4059        {
4060          (void) FormatLocaleString(msg,MagickPathExtent,
4061             "%d were found", num_raw_profiles);
4062          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
4063                 exception);
4064        }
4065
4066      /* cHRM chunk: */
4067      if (ping_found_cHRM != MagickFalse)
4068        {
4069          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4070             "chunk was found (see Chromaticity, above)");
4071          (void) SetImageProperty(image,"png:cHRM",msg,
4072                 exception);
4073        }
4074
4075      /* bKGD chunk: */
4076      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
4077        {
4078          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4079             "chunk was found (see Background color, above)");
4080          (void) SetImageProperty(image,"png:bKGD",msg,
4081                 exception);
4082        }
4083
4084      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
4085         "chunk was found");
4086
4087 #if defined(PNG_iCCP_SUPPORTED)
4088      /* iCCP chunk: */
4089      if (ping_found_iCCP != MagickFalse)
4090         (void) SetImageProperty(image,"png:iCCP",msg,
4091                 exception);
4092 #endif
4093
4094      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
4095         (void) SetImageProperty(image,"png:tRNS",msg,
4096                 exception);
4097
4098 #if defined(PNG_sRGB_SUPPORTED)
4099      /* sRGB chunk: */
4100      if (ping_found_sRGB != MagickFalse)
4101        {
4102          (void) FormatLocaleString(msg,MagickPathExtent,
4103             "intent=%d (%s)",
4104             (int) intent,
4105             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
4106          (void) SetImageProperty(image,"png:sRGB",msg,
4107                  exception);
4108        }
4109 #endif
4110
4111      /* gAMA chunk: */
4112      if (ping_found_gAMA != MagickFalse)
4113        {
4114          (void) FormatLocaleString(msg,MagickPathExtent,
4115             "gamma=%.8g (See Gamma, above)",
4116             file_gamma);
4117          (void) SetImageProperty(image,"png:gAMA",msg,
4118                 exception);
4119        }
4120
4121 #if defined(PNG_pHYs_SUPPORTED)
4122      /* pHYs chunk: */
4123      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4124        {
4125          (void) FormatLocaleString(msg,MagickPathExtent,
4126             "x_res=%.10g, y_res=%.10g, units=%d",
4127             (double) x_resolution,(double) y_resolution, unit_type);
4128          (void) SetImageProperty(image,"png:pHYs",msg,
4129                 exception);
4130        }
4131 #endif
4132
4133 #if defined(PNG_oFFs_SUPPORTED)
4134      /* oFFs chunk: */
4135      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4136        {
4137          (void) FormatLocaleString(msg,MagickPathExtent,
4138             "x_off=%.20g, y_off=%.20g",
4139             (double) image->page.x,(double) image->page.y);
4140          (void) SetImageProperty(image,"png:oFFs",msg,
4141                 exception);
4142        }
4143 #endif
4144
4145 #if defined(PNG_tIME_SUPPORTED)
4146      read_tIME_chunk(image,ping,end_info,exception);
4147 #endif
4148
4149      /* caNv chunk: */
4150      if ((image->page.width != 0 && image->page.width != image->columns) ||
4151          (image->page.height != 0 && image->page.height != image->rows) ||
4152          (image->page.x != 0 || image->page.y != 0))
4153        {
4154          (void) FormatLocaleString(msg,MagickPathExtent,
4155             "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4156             (double) image->page.width,(double) image->page.height,
4157             (double) image->page.x,(double) image->page.y);
4158          (void) SetImageProperty(image,"png:caNv",msg,
4159                 exception);
4160        }
4161    }
4162
4163   /*
4164     Relinquish resources.
4165   */
4166   png_destroy_read_struct(&ping,&ping_info,&end_info);
4167
4168   pixel_info=RelinquishVirtualMemory(pixel_info);
4169
4170   if (logging != MagickFalse)
4171     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4172       "  exit ReadOnePNGImage()");
4173
4174 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4175   UnlockSemaphoreInfo(ping_semaphore);
4176 #endif
4177
4178   /* }  for navigation to beginning of SETJMP-protected block, revert to
4179    *    Throwing an Exception when an error occurs.
4180    */
4181
4182   return(image);
4183
4184 /* end of reading one PNG image */
4185 }
4186
4187 static Image *ReadPNGImage(const ImageInfo *image_info,
4188   ExceptionInfo *exception)
4189 {
4190   Image
4191     *image;
4192
4193   MagickBooleanType
4194     logging,
4195     status;
4196
4197   MngInfo
4198     *mng_info;
4199
4200   char
4201     magic_number[MagickPathExtent];
4202
4203   ssize_t
4204     count;
4205
4206   /*
4207     Open image file.
4208   */
4209   assert(image_info != (const ImageInfo *) NULL);
4210   assert(image_info->signature == MagickCoreSignature);
4211
4212   if (image_info->debug != MagickFalse)
4213     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4214       image_info->filename);
4215
4216   assert(exception != (ExceptionInfo *) NULL);
4217   assert(exception->signature == MagickCoreSignature);
4218   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4219   image=AcquireImage(image_info,exception);
4220   mng_info=(MngInfo *) NULL;
4221   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4222
4223   if (status == MagickFalse)
4224     ThrowReaderException(FileOpenError,"UnableToOpenFile");
4225
4226   /*
4227     Verify PNG signature.
4228   */
4229   count=ReadBlob(image,8,(unsigned char *) magic_number);
4230
4231   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4232     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4233
4234   /*
4235      Verify that file size large enough to contain a PNG datastream.
4236   */
4237   if (GetBlobSize(image) < 61)
4238     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4239
4240   /*
4241     Allocate a MngInfo structure.
4242   */
4243   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4244
4245   if (mng_info == (MngInfo *) NULL)
4246     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4247
4248   /*
4249     Initialize members of the MngInfo structure.
4250   */
4251   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4252   mng_info->image=image;
4253
4254   image=ReadOnePNGImage(mng_info,image_info,exception);
4255   mng_info=MngInfoFreeStruct(mng_info);
4256
4257   if (image == (Image *) NULL)
4258     {
4259       if (logging != MagickFalse)
4260         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4261           "exit ReadPNGImage() with error");
4262
4263       return((Image *) NULL);
4264     }
4265
4266   (void) CloseBlob(image);
4267
4268   if ((image->columns == 0) || (image->rows == 0))
4269     {
4270       if (logging != MagickFalse)
4271         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4272           "exit ReadPNGImage() with error.");
4273
4274       ThrowReaderException(CorruptImageError,"CorruptImage");
4275     }
4276
4277   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4278       ((image->gamma < .45) || (image->gamma > .46)) &&
4279            !(image->chromaticity.red_primary.x>0.6399f &&
4280            image->chromaticity.red_primary.x<0.6401f &&
4281            image->chromaticity.red_primary.y>0.3299f &&
4282            image->chromaticity.red_primary.y<0.3301f &&
4283            image->chromaticity.green_primary.x>0.2999f &&
4284            image->chromaticity.green_primary.x<0.3001f &&
4285            image->chromaticity.green_primary.y>0.5999f &&
4286            image->chromaticity.green_primary.y<0.6001f &&
4287            image->chromaticity.blue_primary.x>0.1499f &&
4288            image->chromaticity.blue_primary.x<0.1501f &&
4289            image->chromaticity.blue_primary.y>0.0599f &&
4290            image->chromaticity.blue_primary.y<0.0601f &&
4291            image->chromaticity.white_point.x>0.3126f &&
4292            image->chromaticity.white_point.x<0.3128f &&
4293            image->chromaticity.white_point.y>0.3289f &&
4294            image->chromaticity.white_point.y<0.3291f))
4295     {
4296        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4297           "SetImageColorspace to RGBColorspace");
4298        SetImageColorspace(image,RGBColorspace,exception);
4299     }
4300
4301   if (logging != MagickFalse)
4302     {
4303        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4304            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4305                (double) image->page.width,(double) image->page.height,
4306                (double) image->page.x,(double) image->page.y);
4307        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4308            "  image->colorspace: %d", (int) image->colorspace);
4309     }
4310
4311   if (logging != MagickFalse)
4312     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4313
4314   return(image);
4315 }
4316
4317
4318
4319 #if defined(JNG_SUPPORTED)
4320 /*
4321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4322 %                                                                             %
4323 %                                                                             %
4324 %                                                                             %
4325 %   R e a d O n e J N G I m a g e                                             %
4326 %                                                                             %
4327 %                                                                             %
4328 %                                                                             %
4329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4330 %
4331 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4332 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4333 %  necessary for the new Image structure and returns a pointer to the new
4334 %  image.
4335 %
4336 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4337 %
4338 %  The format of the ReadOneJNGImage method is:
4339 %
4340 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4341 %         ExceptionInfo *exception)
4342 %
4343 %  A description of each parameter follows:
4344 %
4345 %    o mng_info: Specifies a pointer to a MngInfo structure.
4346 %
4347 %    o image_info: the image info.
4348 %
4349 %    o exception: return any errors or warnings in this structure.
4350 %
4351 */
4352 void
4353 DestroyJNG(unsigned char *chunk,Image **color_image,
4354    ImageInfo **color_image_info,
4355    Image **alpha_image,ImageInfo **alpha_image_info)
4356 {
4357   (void) RelinquishMagickMemory(chunk);
4358   if (*color_image_info)
4359   {
4360     DestroyImageInfo(*color_image_info);
4361     *color_image_info = (ImageInfo *)NULL;
4362   }
4363   if (*alpha_image_info)
4364   {
4365     DestroyImageInfo(*alpha_image_info);
4366     *alpha_image_info = (ImageInfo *)NULL;
4367   }
4368   if (*color_image)
4369   {
4370     DestroyImage(*color_image);
4371     *color_image = (Image *)NULL;
4372   }
4373   if (*alpha_image)
4374   {
4375     DestroyImage(*alpha_image);
4376     *alpha_image = (Image *)NULL;
4377   }
4378 }
4379 static Image *ReadOneJNGImage(MngInfo *mng_info,
4380     const ImageInfo *image_info, ExceptionInfo *exception)
4381 {
4382   Image
4383     *alpha_image,
4384     *color_image,
4385     *image,
4386     *jng_image;
4387
4388   ImageInfo
4389     *alpha_image_info,
4390     *color_image_info;
4391
4392   MagickBooleanType
4393     logging;
4394
4395   ssize_t
4396     y;
4397
4398   MagickBooleanType
4399     status;
4400
4401   png_uint_32
4402     jng_height,
4403     jng_width;
4404
4405   png_byte
4406     jng_color_type,
4407     jng_image_sample_depth,
4408     jng_image_compression_method,
4409     jng_image_interlace_method,
4410     jng_alpha_sample_depth,
4411     jng_alpha_compression_method,
4412     jng_alpha_filter_method,
4413     jng_alpha_interlace_method;
4414
4415   register const Quantum
4416     *s;
4417
4418   register ssize_t
4419     i,
4420     x;
4421
4422   register Quantum
4423     *q;
4424
4425   register unsigned char
4426     *p;
4427
4428   unsigned int
4429     read_JSEP,
4430     reading_idat;
4431
4432   size_t
4433     length;
4434
4435   jng_alpha_compression_method=0;
4436   jng_alpha_sample_depth=8;
4437   jng_color_type=0;
4438   jng_height=0;
4439   jng_width=0;
4440   alpha_image=(Image *) NULL;
4441   color_image=(Image *) NULL;
4442   alpha_image_info=(ImageInfo *) NULL;
4443   color_image_info=(ImageInfo *) NULL;
4444
4445   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4446     "  Enter ReadOneJNGImage()");
4447
4448   image=mng_info->image;
4449
4450   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4451     {
4452       /*
4453         Allocate next image structure.
4454       */
4455       if (logging != MagickFalse)
4456         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4457            "  AcquireNextImage()");
4458
4459       AcquireNextImage(image_info,image,exception);
4460
4461       if (GetNextImageInList(image) == (Image *) NULL)
4462         return(DestroyImageList(image));
4463
4464       image=SyncNextImageInList(image);
4465     }
4466   mng_info->image=image;
4467
4468   /*
4469     Signature bytes have already been read.
4470   */
4471
4472   read_JSEP=MagickFalse;
4473   reading_idat=MagickFalse;
4474   for (;;)
4475   {
4476     char
4477       type[MagickPathExtent];
4478
4479     unsigned char
4480       *chunk;
4481
4482     unsigned int
4483       count;
4484
4485     /*
4486       Read a new JNG chunk.
4487     */
4488     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4489       2*GetBlobSize(image));
4490
4491     if (status == MagickFalse)
4492       break;
4493
4494     type[0]='\0';
4495     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4496     length=ReadBlobMSBLong(image);
4497     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4498
4499     if (logging != MagickFalse)
4500       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4501         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4502         type[0],type[1],type[2],type[3],(double) length);
4503
4504     if (length > PNG_UINT_31_MAX || count == 0)
4505       {
4506         DestroyJNG(NULL,&color_image,&color_image_info,
4507           &alpha_image,&alpha_image_info);
4508         ThrowReaderException(CorruptImageError,"CorruptImage");
4509       }
4510
4511     p=NULL;
4512     chunk=(unsigned char *) NULL;
4513
4514     if (length != 0)
4515       {
4516         if (length > GetBlobSize(image))
4517           {
4518             DestroyJNG(NULL,&color_image,&color_image_info,
4519               &alpha_image,&alpha_image_info);
4520             ThrowReaderException(CorruptImageError,
4521               "InsufficientImageDataInFile");
4522           }
4523         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4524
4525         if (chunk == (unsigned char *) NULL)
4526           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4527
4528         for (i=0; i < (ssize_t) length; i++)
4529         {
4530           int
4531             c;
4532
4533           c=ReadBlobByte(image);
4534           if (c == EOF)
4535             break;
4536           chunk[i]=(unsigned char) c;
4537         }
4538
4539         p=chunk;
4540       }
4541
4542     (void) ReadBlobMSBLong(image);  /* read crc word */
4543
4544     if (memcmp(type,mng_JHDR,4) == 0)
4545       {
4546         if (length == 16)
4547           {
4548             jng_width=(png_uint_32)mng_get_long(p);
4549             jng_height=(png_uint_32)mng_get_long(&p[4]);
4550             if ((jng_width == 0) || (jng_height == 0))
4551               ThrowReaderException(CorruptImageError,
4552                 "NegativeOrZeroImageSize");
4553             jng_color_type=p[8];
4554             jng_image_sample_depth=p[9];
4555             jng_image_compression_method=p[10];
4556             jng_image_interlace_method=p[11];
4557
4558             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4559               NoInterlace;
4560
4561             jng_alpha_sample_depth=p[12];
4562             jng_alpha_compression_method=p[13];
4563             jng_alpha_filter_method=p[14];
4564             jng_alpha_interlace_method=p[15];
4565
4566             if (logging != MagickFalse)
4567               {
4568                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4569                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4570                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4571                   "    jng_image_compression_method:%3d",
4572                   (unsigned long) jng_width, (unsigned long) jng_height,
4573                   jng_color_type, jng_image_sample_depth,
4574                   jng_image_compression_method);
4575
4576                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4577                   "    jng_image_interlace_method:  %3d"
4578                   "    jng_alpha_sample_depth:      %3d",
4579                   jng_image_interlace_method,
4580                   jng_alpha_sample_depth);
4581
4582                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4583                   "    jng_alpha_compression_method:%3d\n"
4584                   "    jng_alpha_filter_method:     %3d\n"
4585                   "    jng_alpha_interlace_method:  %3d",
4586                   jng_alpha_compression_method,
4587                   jng_alpha_filter_method,
4588                   jng_alpha_interlace_method);
4589               }
4590           }
4591
4592         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4593
4594         if (jng_width > 65535 || jng_height > 65535 ||
4595              (long) jng_width > GetMagickResourceLimit(WidthResource) ||
4596              (long) jng_height > GetMagickResourceLimit(HeightResource))
4597           {
4598             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4599                "    JNG width or height too large: (%lu x %lu)",
4600                 (long) jng_width, (long) jng_height);
4601             DestroyJNG(chunk,&color_image,&color_image_info,
4602               &alpha_image,&alpha_image_info);
4603             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4604           }
4605
4606         continue;
4607       }
4608
4609
4610     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4611         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4612          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4613       {
4614         /*
4615            o create color_image
4616            o open color_blob, attached to color_image
4617            o if (color type has alpha)
4618                open alpha_blob, attached to alpha_image
4619         */
4620
4621         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4622
4623         if (color_image_info == (ImageInfo *) NULL)
4624           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4625
4626         GetImageInfo(color_image_info);
4627         color_image=AcquireImage(color_image_info,exception);
4628
4629         if (color_image == (Image *) NULL)
4630           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4631
4632         if (logging != MagickFalse)
4633           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4634             "    Creating color_blob.");
4635
4636         (void) AcquireUniqueFilename(color_image->filename);
4637         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4638           exception);
4639
4640         if (status == MagickFalse)
4641           {
4642             color_image=DestroyImage(color_image);
4643             return(DestroyImageList(image));
4644           }
4645
4646         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4647           {
4648             alpha_image_info=(ImageInfo *)
4649               AcquireMagickMemory(sizeof(ImageInfo));
4650
4651             if (alpha_image_info == (ImageInfo *) NULL)
4652               {
4653                 color_image=DestroyImage(color_image);
4654                 ThrowReaderException(ResourceLimitError,
4655                   "MemoryAllocationFailed");
4656               }
4657
4658             GetImageInfo(alpha_image_info);
4659             alpha_image=AcquireImage(alpha_image_info,exception);
4660
4661             if (alpha_image == (Image *) NULL)
4662               {
4663                 alpha_image_info=DestroyImageInfo(alpha_image_info);
4664                 color_image=DestroyImage(color_image);
4665                 ThrowReaderException(ResourceLimitError,
4666                   "MemoryAllocationFailed");
4667               }
4668
4669             if (logging != MagickFalse)
4670               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4671                 "    Creating alpha_blob.");
4672
4673             (void) AcquireUniqueFilename(alpha_image->filename);
4674             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4675               exception);
4676
4677             if (status == MagickFalse)
4678               {
4679                 alpha_image=DestroyImage(alpha_image);
4680                 alpha_image_info=DestroyImageInfo(alpha_image_info);
4681                 color_image=DestroyImage(color_image);
4682                 return(DestroyImageList(image));
4683               }
4684
4685             if (jng_alpha_compression_method == 0)
4686               {
4687                 unsigned char
4688                   data[18];
4689
4690                 if (logging != MagickFalse)
4691                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4692                     "    Writing IHDR chunk to alpha_blob.");
4693
4694                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4695                   "\211PNG\r\n\032\n");
4696
4697                 (void) WriteBlobMSBULong(alpha_image,13L);
4698                 PNGType(data,mng_IHDR);
4699                 LogPNGChunk(logging,mng_IHDR,13L);
4700                 PNGLong(data+4,jng_width);
4701                 PNGLong(data+8,jng_height);
4702                 data[12]=jng_alpha_sample_depth;
4703                 data[13]=0; /* color_type gray */
4704                 data[14]=0; /* compression method 0 */
4705                 data[15]=0; /* filter_method 0 */
4706                 data[16]=0; /* interlace_method 0 */
4707                 (void) WriteBlob(alpha_image,17,data);
4708                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4709               }
4710           }
4711         reading_idat=MagickTrue;
4712       }
4713
4714     if (memcmp(type,mng_JDAT,4) == 0)
4715       {
4716         /* Copy chunk to color_image->blob */
4717
4718         if (logging != MagickFalse)
4719           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4720             "    Copying JDAT chunk data to color_blob.");
4721
4722         if (length != 0)
4723           {
4724             (void) WriteBlob(color_image,length,chunk);
4725             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4726           }
4727
4728         continue;
4729       }
4730
4731     if (memcmp(type,mng_IDAT,4) == 0)
4732       {
4733         png_byte
4734            data[5];
4735
4736         /* Copy IDAT header and chunk data to alpha_image->blob */
4737
4738         if (alpha_image != NULL && image_info->ping == MagickFalse)
4739           {
4740             if (logging != MagickFalse)
4741               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4742                 "    Copying IDAT chunk data to alpha_blob.");
4743
4744             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4745             PNGType(data,mng_IDAT);
4746             LogPNGChunk(logging,mng_IDAT,length);
4747             (void) WriteBlob(alpha_image,4,data);
4748             (void) WriteBlob(alpha_image,length,chunk);
4749             (void) WriteBlobMSBULong(alpha_image,
4750               crc32(crc32(0,data,4),chunk,(uInt) length));
4751           }
4752
4753         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4754
4755         continue;
4756       }
4757
4758     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4759       {
4760         /* Copy chunk data to alpha_image->blob */
4761
4762         if (alpha_image != NULL && image_info->ping == MagickFalse)
4763           {
4764             if (logging != MagickFalse)
4765               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4766                 "    Copying JDAA chunk data to alpha_blob.");
4767
4768             (void) WriteBlob(alpha_image,length,chunk);
4769           }
4770
4771         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4772
4773         continue;
4774       }
4775
4776     if (memcmp(type,mng_JSEP,4) == 0)
4777       {
4778         read_JSEP=MagickTrue;
4779
4780         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4781
4782         continue;
4783       }
4784
4785     if (memcmp(type,mng_bKGD,4) == 0)
4786       {
4787         if (length == 2)
4788           {
4789             image->background_color.red=ScaleCharToQuantum(p[1]);
4790             image->background_color.green=image->background_color.red;
4791             image->background_color.blue=image->background_color.red;
4792           }
4793
4794         if (length == 6)
4795           {
4796             image->background_color.red=ScaleCharToQuantum(p[1]);
4797             image->background_color.green=ScaleCharToQuantum(p[3]);
4798             image->background_color.blue=ScaleCharToQuantum(p[5]);
4799           }
4800
4801         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4802         continue;
4803       }
4804
4805     if (memcmp(type,mng_gAMA,4) == 0)
4806       {
4807         if (length == 4)
4808           image->gamma=((float) mng_get_long(p))*0.00001;
4809
4810         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4811         continue;
4812       }
4813
4814     if (memcmp(type,mng_cHRM,4) == 0)
4815       {
4816         if (length == 32)
4817           {
4818             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4819             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4820             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4821             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4822             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4823             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4824             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4825             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4826           }
4827
4828         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4829         continue;
4830       }
4831
4832     if (memcmp(type,mng_sRGB,4) == 0)
4833       {
4834         if (length == 1)
4835           {
4836             image->rendering_intent=
4837               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4838             image->gamma=1.000f/2.200f;
4839             image->chromaticity.red_primary.x=0.6400f;
4840             image->chromaticity.red_primary.y=0.3300f;
4841             image->chromaticity.green_primary.x=0.3000f;
4842             image->chromaticity.green_primary.y=0.6000f;
4843             image->chromaticity.blue_primary.x=0.1500f;
4844             image->chromaticity.blue_primary.y=0.0600f;
4845             image->chromaticity.white_point.x=0.3127f;
4846             image->chromaticity.white_point.y=0.3290f;
4847           }
4848
4849         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4850         continue;
4851       }
4852
4853     if (memcmp(type,mng_oFFs,4) == 0)
4854       {
4855         if (length > 8)
4856           {
4857             image->page.x=(ssize_t) mng_get_long(p);
4858             image->page.y=(ssize_t) mng_get_long(&p[4]);
4859
4860             if ((int) p[8] != 0)
4861               {
4862                 image->page.x/=10000;
4863                 image->page.y/=10000;
4864               }
4865           }
4866
4867         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4868
4869         continue;
4870       }
4871
4872     if (memcmp(type,mng_pHYs,4) == 0)
4873       {
4874         if (length > 8)
4875           {
4876             image->resolution.x=(double) mng_get_long(p);
4877             image->resolution.y=(double) mng_get_long(&p[4]);
4878             if ((int) p[8] == PNG_RESOLUTION_METER)
4879               {
4880                 image->units=PixelsPerCentimeterResolution;
4881                 image->resolution.x=image->resolution.x/100.0f;
4882                 image->resolution.y=image->resolution.y/100.0f;
4883               }
4884           }
4885
4886         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4887         continue;
4888       }
4889
4890 #if 0
4891     if (memcmp(type,mng_iCCP,4) == 0)
4892       {
4893         /* To do: */
4894         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4895
4896         continue;
4897       }
4898 #endif
4899
4900     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4901
4902     if (memcmp(type,mng_IEND,4))
4903       continue;
4904
4905     break;
4906   }
4907
4908
4909   /* IEND found */
4910
4911   /*
4912     Finish up reading image data:
4913
4914        o read main image from color_blob.
4915
4916        o close color_blob.
4917
4918        o if (color_type has alpha)
4919             if alpha_encoding is PNG
4920                read secondary image from alpha_blob via ReadPNG
4921             if alpha_encoding is JPEG
4922                read secondary image from alpha_blob via ReadJPEG
4923
4924        o close alpha_blob.
4925
4926        o copy intensity of secondary image into
4927          alpha samples of main image.
4928
4929        o destroy the secondary image.
4930   */
4931
4932   if (color_image_info == (ImageInfo *) NULL)
4933     {
4934       assert(color_image == (Image *) NULL);
4935       assert(alpha_image == (Image *) NULL);
4936       return(DestroyImageList(image));
4937     }
4938
4939   if (color_image == (Image *) NULL)
4940     {
4941       assert(alpha_image == (Image *) NULL);
4942       return(DestroyImageList(image));
4943     }
4944
4945   (void) SeekBlob(color_image,0,SEEK_SET);
4946
4947   if (logging != MagickFalse)
4948     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4949       "    Reading jng_image from color_blob.");
4950
4951   assert(color_image_info != (ImageInfo *) NULL);
4952   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4953     color_image->filename);
4954
4955   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4956   jng_image=ReadImage(color_image_info,exception);
4957
4958   (void) RelinquishUniqueFileResource(color_image->filename);
4959   color_image=DestroyImage(color_image);
4960   color_image_info=DestroyImageInfo(color_image_info);
4961
4962   if (jng_image == (Image *) NULL)
4963     return(DestroyImageList(image));
4964
4965   if (logging != MagickFalse)
4966     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4967       "    Copying jng_image pixels to main image.");
4968
4969   image->rows=jng_height;
4970   image->columns=jng_width;
4971
4972   status=SetImageExtent(image,image->columns,image->rows,exception);
4973   if (status == MagickFalse)
4974     return(DestroyImageList(image));
4975
4976   for (y=0; y < (ssize_t) image->rows; y++)
4977   {
4978     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4979     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4980     for (x=(ssize_t) image->columns; x != 0; x--)
4981     {
4982       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4983       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4984       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4985       q+=GetPixelChannels(image);
4986       s+=GetPixelChannels(jng_image);
4987     }
4988
4989     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4990       break;
4991   }
4992
4993   jng_image=DestroyImage(jng_image);
4994
4995   if (image_info->ping == MagickFalse)
4996     {
4997      if (jng_color_type >= 12)
4998        {
4999          if (jng_alpha_compression_method == 0)
5000            {
5001              png_byte
5002                data[5];
5003              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
5004              PNGType(data,mng_IEND);
5005              LogPNGChunk(logging,mng_IEND,0L);
5006              (void) WriteBlob(alpha_image,4,data);
5007              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
5008            }
5009
5010          (void) CloseBlob(alpha_image);
5011
5012          if (logging != MagickFalse)
5013            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5014              "    Reading alpha from alpha_blob.");
5015
5016          (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
5017            "%s",alpha_image->filename);
5018
5019          jng_image=ReadImage(alpha_image_info,exception);
5020
5021          if (jng_image != (Image *) NULL)
5022            for (y=0; y < (ssize_t) image->rows; y++)
5023            {
5024              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
5025                exception);
5026              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
5027
5028              if (image->alpha_trait != UndefinedPixelTrait)
5029                for (x=(ssize_t) image->columns; x != 0; x--)
5030                {
5031                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5032                   q+=GetPixelChannels(image);
5033                   s+=GetPixelChannels(jng_image);
5034                }
5035
5036              else
5037                for (x=(ssize_t) image->columns; x != 0; x--)
5038                {
5039                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
5040                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
5041                     image->alpha_trait=BlendPixelTrait;
5042                   q+=GetPixelChannels(image);
5043                   s+=GetPixelChannels(jng_image);
5044                }
5045
5046              if (SyncAuthenticPixels(image,exception) == MagickFalse)
5047                break;
5048            }
5049          (void) RelinquishUniqueFileResource(alpha_image->filename);
5050          alpha_image=DestroyImage(alpha_image);
5051          alpha_image_info=DestroyImageInfo(alpha_image_info);
5052          if (jng_image != (Image *) NULL)
5053            jng_image=DestroyImage(jng_image);
5054        }
5055     }
5056
5057   /* Read the JNG image.  */
5058
5059   if (mng_info->mng_type == 0)
5060     {
5061       mng_info->mng_width=jng_width;
5062       mng_info->mng_height=jng_height;
5063     }
5064
5065   if (image->page.width == 0 && image->page.height == 0)
5066     {
5067       image->page.width=jng_width;
5068       image->page.height=jng_height;
5069     }
5070
5071   if (image->page.x == 0 && image->page.y == 0)
5072     {
5073       image->page.x=mng_info->x_off[mng_info->object_id];
5074       image->page.y=mng_info->y_off[mng_info->object_id];
5075     }
5076
5077   else
5078     {
5079       image->page.y=mng_info->y_off[mng_info->object_id];
5080     }
5081
5082   mng_info->image_found++;
5083   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
5084     2*GetBlobSize(image));
5085
5086   if (status == MagickFalse)
5087     return(DestroyImageList(image));
5088
5089   if (logging != MagickFalse)
5090     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5091       "  exit ReadOneJNGImage()");
5092
5093   return(image);
5094 }
5095
5096 /*
5097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5098 %                                                                             %
5099 %                                                                             %
5100 %                                                                             %
5101 %   R e a d J N G I m a g e                                                   %
5102 %                                                                             %
5103 %                                                                             %
5104 %                                                                             %
5105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5106 %
5107 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
5108 %  (including the 8-byte signature)  and returns it.  It allocates the memory
5109 %  necessary for the new Image structure and returns a pointer to the new
5110 %  image.
5111 %
5112 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
5113 %
5114 %  The format of the ReadJNGImage method is:
5115 %
5116 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5117 %         *exception)
5118 %
5119 %  A description of each parameter follows:
5120 %
5121 %    o image_info: the image info.
5122 %
5123 %    o exception: return any errors or warnings in this structure.
5124 %
5125 */
5126
5127 static Image *ReadJNGImage(const ImageInfo *image_info,
5128                 ExceptionInfo *exception)
5129 {
5130   Image
5131     *image;
5132
5133   MagickBooleanType
5134     logging,
5135     status;
5136
5137   MngInfo
5138     *mng_info;
5139
5140   char
5141     magic_number[MagickPathExtent];
5142
5143   size_t
5144     count;
5145
5146   /*
5147     Open image file.
5148   */
5149   assert(image_info != (const ImageInfo *) NULL);
5150   assert(image_info->signature == MagickCoreSignature);
5151   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5152      image_info->filename);
5153   assert(exception != (ExceptionInfo *) NULL);
5154   assert(exception->signature == MagickCoreSignature);
5155   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5156   image=AcquireImage(image_info,exception);
5157   mng_info=(MngInfo *) NULL;
5158   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5159
5160   if (status == MagickFalse)
5161     return((Image *) NULL);
5162
5163   if (LocaleCompare(image_info->magick,"JNG") != 0)
5164     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5165
5166   /* Verify JNG signature.  */
5167
5168   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5169
5170   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5171     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5172
5173   /*
5174      Verify that file size large enough to contain a JNG datastream.
5175   */
5176   if (GetBlobSize(image) < 147)
5177     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5178
5179   /* Allocate a MngInfo structure.  */
5180
5181   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5182
5183   if (mng_info == (MngInfo *) NULL)
5184     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5185
5186   /* Initialize members of the MngInfo structure.  */
5187
5188   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5189
5190   mng_info->image=image;
5191   image=ReadOneJNGImage(mng_info,image_info,exception);
5192   mng_info=MngInfoFreeStruct(mng_info);
5193
5194   if (image == (Image *) NULL)
5195     {
5196       if (logging != MagickFalse)
5197         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5198           "exit ReadJNGImage() with error");
5199
5200       return((Image *) NULL);
5201     }
5202   (void) CloseBlob(image);
5203
5204   if (image->columns == 0 || image->rows == 0)
5205     {
5206       if (logging != MagickFalse)
5207         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5208           "exit ReadJNGImage() with error");
5209
5210       ThrowReaderException(CorruptImageError,"CorruptImage");
5211     }
5212
5213   if (logging != MagickFalse)
5214     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5215
5216   return(image);
5217 }
5218 #endif
5219
5220 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5221      ExceptionInfo *exception)
5222 {
5223   char
5224     page_geometry[MagickPathExtent];
5225
5226   Image
5227     *image;
5228
5229   MagickBooleanType
5230     logging;
5231
5232   volatile int
5233     first_mng_object,
5234     object_id,
5235     term_chunk_found,
5236     skip_to_iend;
5237
5238   volatile ssize_t
5239     image_count=0;
5240
5241   MagickBooleanType
5242     status;
5243
5244   MagickOffsetType
5245     offset;
5246
5247   MngBox
5248     default_fb,
5249     fb,
5250     previous_fb;
5251
5252 #if defined(MNG_INSERT_LAYERS)
5253   PixelInfo
5254     mng_background_color;
5255 #endif
5256
5257   register unsigned char
5258     *p;
5259
5260   register ssize_t
5261     i;
5262
5263   size_t
5264     count;
5265
5266   ssize_t
5267     loop_level;
5268
5269   volatile short
5270     skipping_loop;
5271
5272 #if defined(MNG_INSERT_LAYERS)
5273   unsigned int
5274     mandatory_back=0;
5275 #endif
5276
5277   volatile unsigned int
5278 #ifdef MNG_OBJECT_BUFFERS
5279     mng_background_object=0,
5280 #endif
5281     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5282
5283   size_t
5284     default_frame_timeout,
5285     frame_timeout,
5286 #if defined(MNG_INSERT_LAYERS)
5287     image_height,
5288     image_width,
5289 #endif
5290     length;
5291
5292   /* These delays are all measured in image ticks_per_second,
5293    * not in MNG ticks_per_second
5294    */
5295   volatile size_t
5296     default_frame_delay,
5297     final_delay,
5298     final_image_delay,
5299     frame_delay,
5300 #if defined(MNG_INSERT_LAYERS)
5301     insert_layers,
5302 #endif
5303     mng_iterations=1,
5304     simplicity=0,
5305     subframe_height=0,
5306     subframe_width=0;
5307
5308   previous_fb.top=0;
5309   previous_fb.bottom=0;
5310   previous_fb.left=0;
5311   previous_fb.right=0;
5312   default_fb.top=0;
5313   default_fb.bottom=0;
5314   default_fb.left=0;
5315   default_fb.right=0;
5316
5317   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5318     "  Enter ReadOneMNGImage()");
5319
5320   image=mng_info->image;
5321
5322   if (LocaleCompare(image_info->magick,"MNG") == 0)
5323     {
5324       char
5325         magic_number[MagickPathExtent];
5326
5327       /* Verify MNG signature.  */
5328       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5329       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5330         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5331
5332       /* Initialize some nonzero members of the MngInfo structure.  */
5333       for (i=0; i < MNG_MAX_OBJECTS; i++)
5334       {
5335         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5336         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5337       }
5338       mng_info->exists[0]=MagickTrue;
5339     }
5340
5341   skipping_loop=(-1);
5342   first_mng_object=MagickTrue;
5343   mng_type=0;
5344 #if defined(MNG_INSERT_LAYERS)
5345   insert_layers=MagickFalse; /* should be False during convert or mogrify */
5346 #endif
5347   default_frame_delay=0;
5348   default_frame_timeout=0;
5349   frame_delay=0;
5350   final_delay=1;
5351   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5352   object_id=0;
5353   skip_to_iend=MagickFalse;
5354   term_chunk_found=MagickFalse;
5355   mng_info->framing_mode=1;
5356 #if defined(MNG_INSERT_LAYERS)
5357   mandatory_back=MagickFalse;
5358 #endif
5359 #if defined(MNG_INSERT_LAYERS)
5360   mng_background_color=image->background_color;
5361 #endif
5362   default_fb=mng_info->frame;
5363   previous_fb=mng_info->frame;
5364   do
5365   {
5366     char
5367       type[MagickPathExtent];
5368
5369     if (LocaleCompare(image_info->magick,"MNG") == 0)
5370       {
5371         unsigned char
5372           *chunk;
5373
5374         /*
5375           Read a new chunk.
5376         */
5377         type[0]='\0';
5378         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5379         length=ReadBlobMSBLong(image);
5380         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5381
5382         if (logging != MagickFalse)
5383           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5384            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5385            type[0],type[1],type[2],type[3],(double) length);
5386
5387         if (length > PNG_UINT_31_MAX)
5388           {
5389             status=MagickFalse;
5390             break;
5391           }
5392
5393         if (count == 0)
5394           ThrowReaderException(CorruptImageError,"CorruptImage");
5395
5396         p=NULL;
5397         chunk=(unsigned char *) NULL;
5398
5399         if (length != 0)
5400           {
5401             if (length > GetBlobSize(image))
5402               ThrowReaderException(CorruptImageError,
5403                 "InsufficientImageDataInFile");
5404             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5405
5406             if (chunk == (unsigned char *) NULL)
5407               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5408
5409             for (i=0; i < (ssize_t) length; i++)
5410             {
5411               int
5412                 c;
5413
5414               c=ReadBlobByte(image);
5415               if (c == EOF)
5416                 break;
5417               chunk[i]=(unsigned char) c;
5418             }
5419
5420             p=chunk;
5421           }
5422
5423         (void) ReadBlobMSBLong(image);  /* read crc word */
5424
5425 #if !defined(JNG_SUPPORTED)
5426         if (memcmp(type,mng_JHDR,4) == 0)
5427           {
5428             skip_to_iend=MagickTrue;
5429
5430             if (mng_info->jhdr_warning == 0)
5431               (void) ThrowMagickException(exception,GetMagickModule(),
5432                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5433
5434             mng_info->jhdr_warning++;
5435           }
5436 #endif
5437         if (memcmp(type,mng_DHDR,4) == 0)
5438           {
5439             skip_to_iend=MagickTrue;
5440
5441             if (mng_info->dhdr_warning == 0)
5442               (void) ThrowMagickException(exception,GetMagickModule(),
5443                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5444
5445             mng_info->dhdr_warning++;
5446           }
5447         if (memcmp(type,mng_MEND,4) == 0)
5448           {
5449             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5450             break;
5451           }
5452
5453         if (skip_to_iend)
5454           {
5455             if (memcmp(type,mng_IEND,4) == 0)
5456               skip_to_iend=MagickFalse;
5457
5458             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5459
5460             if (logging != MagickFalse)
5461               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5462                 "  Skip to IEND.");
5463
5464             continue;
5465           }
5466
5467         if (memcmp(type,mng_MHDR,4) == 0)
5468           {
5469             if (length != 28)
5470               {
5471                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5472                 ThrowReaderException(CorruptImageError,"CorruptImage");
5473               }
5474
5475             mng_info->mng_width=(unsigned long)mng_get_long(p);
5476             mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5477
5478             if (logging != MagickFalse)
5479               {
5480                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5481                   "  MNG width: %.20g",(double) mng_info->mng_width);
5482                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5483                   "  MNG height: %.20g",(double) mng_info->mng_height);
5484               }
5485
5486             p+=8;
5487             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5488
5489             if (mng_info->ticks_per_second == 0)
5490               default_frame_delay=0;
5491
5492             else
5493               default_frame_delay=1UL*image->ticks_per_second/
5494                 mng_info->ticks_per_second;
5495
5496             frame_delay=default_frame_delay;
5497             simplicity=0;
5498
5499             p+=16;
5500             simplicity=(size_t) mng_get_long(p);
5501
5502             mng_type=1;    /* Full MNG */
5503
5504             if ((simplicity != 0) && ((simplicity | 11) == 11))
5505               mng_type=2; /* LC */
5506
5507             if ((simplicity != 0) && ((simplicity | 9) == 9))
5508               mng_type=3; /* VLC */
5509
5510 #if defined(MNG_INSERT_LAYERS)
5511             if (mng_type != 3)
5512               insert_layers=MagickTrue;
5513 #endif
5514             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5515               {
5516                 /* Allocate next image structure.  */
5517                 AcquireNextImage(image_info,image,exception);
5518
5519                 if (GetNextImageInList(image) == (Image *) NULL)
5520                   return((Image *) NULL);
5521
5522                 image=SyncNextImageInList(image);
5523                 mng_info->image=image;
5524               }
5525
5526             if ((mng_info->mng_width > 65535L) ||
5527                 (mng_info->mng_height > 65535L))
5528               {
5529                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5530                 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5531               }
5532
5533             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5534               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5535               mng_info->mng_height);
5536
5537             mng_info->frame.left=0;
5538             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5539             mng_info->frame.top=0;
5540             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5541             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5542
5543             for (i=0; i < MNG_MAX_OBJECTS; i++)
5544               mng_info->object_clip[i]=mng_info->frame;
5545
5546             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5547             continue;
5548           }
5549
5550         if (memcmp(type,mng_TERM,4) == 0)
5551           {
5552             int
5553               repeat=0;
5554
5555             if (length != 0)
5556               repeat=p[0];
5557
5558             if (repeat == 3 && length > 8)
5559               {
5560                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5561                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5562
5563                 if (mng_iterations == PNG_UINT_31_MAX)
5564                   mng_iterations=0;
5565
5566                 image->iterations=mng_iterations;
5567                 term_chunk_found=MagickTrue;
5568               }
5569
5570             if (logging != MagickFalse)
5571               {
5572                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5573                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5574                   repeat,(double) final_delay, (double) image->iterations);
5575               }
5576
5577             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5578             continue;
5579           }
5580         if (memcmp(type,mng_DEFI,4) == 0)
5581           {
5582             if (mng_type == 3)
5583               (void) ThrowMagickException(exception,GetMagickModule(),
5584                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5585                 image->filename);
5586
5587             if (length < 2)
5588               {
5589                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5590                 ThrowReaderException(CorruptImageError,"CorruptImage");
5591               }
5592
5593             object_id=(p[0] << 8) | p[1];
5594
5595             if (mng_type == 2 && object_id != 0)
5596               (void) ThrowMagickException(exception,GetMagickModule(),
5597                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5598                 image->filename);
5599
5600             if (object_id > MNG_MAX_OBJECTS)
5601               {
5602                 /*
5603                   Instead of using a warning we should allocate a larger
5604                   MngInfo structure and continue.
5605                 */
5606                 (void) ThrowMagickException(exception,GetMagickModule(),
5607                   CoderError,"object id too large","`%s'",image->filename);
5608                 object_id=MNG_MAX_OBJECTS;
5609               }
5610
5611             if (mng_info->exists[object_id])
5612               if (mng_info->frozen[object_id])
5613                 {
5614                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5615                   (void) ThrowMagickException(exception,
5616                     GetMagickModule(),CoderError,
5617                     "DEFI cannot redefine a frozen MNG object","`%s'",
5618                     image->filename);
5619                   continue;
5620                 }
5621
5622             mng_info->exists[object_id]=MagickTrue;
5623
5624             if (length > 2)
5625               mng_info->invisible[object_id]=p[2];
5626
5627             /*
5628               Extract object offset info.
5629             */
5630             if (length > 11)
5631               {
5632                 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5633                 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5634                 if (logging != MagickFalse)
5635                   {
5636                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5637                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5638                       object_id,(double) mng_info->x_off[object_id],
5639                       object_id,(double) mng_info->y_off[object_id]);
5640                   }
5641               }
5642
5643             /*
5644               Extract object clipping info.
5645             */
5646             if (length > 27)
5647               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5648                 &p[12]);
5649
5650             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5651             continue;
5652           }
5653         if (memcmp(type,mng_bKGD,4) == 0)
5654           {
5655             mng_info->have_global_bkgd=MagickFalse;
5656
5657             if (length > 5)
5658               {
5659                 mng_info->mng_global_bkgd.red=
5660                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5661
5662                 mng_info->mng_global_bkgd.green=
5663                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5664
5665                 mng_info->mng_global_bkgd.blue=
5666                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5667
5668                 mng_info->have_global_bkgd=MagickTrue;
5669               }
5670
5671             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5672             continue;
5673           }
5674         if (memcmp(type,mng_BACK,4) == 0)
5675           {
5676 #if defined(MNG_INSERT_LAYERS)
5677             if (length > 6)
5678               mandatory_back=p[6];
5679
5680             else
5681               mandatory_back=0;
5682
5683             if (mandatory_back && length > 5)
5684               {
5685                 mng_background_color.red=
5686                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5687
5688                 mng_background_color.green=
5689                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5690
5691                 mng_background_color.blue=
5692                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5693
5694                 mng_background_color.alpha=OpaqueAlpha;
5695               }
5696
5697 #ifdef MNG_OBJECT_BUFFERS
5698             if (length > 8)
5699               mng_background_object=(p[7] << 8) | p[8];
5700 #endif
5701 #endif
5702             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5703             continue;
5704           }
5705
5706         if (memcmp(type,mng_PLTE,4) == 0)
5707           {
5708             /* Read global PLTE.  */
5709
5710             if (length && (length < 769))
5711               {
5712                 if (mng_info->global_plte == (png_colorp) NULL)
5713                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5714                     sizeof(*mng_info->global_plte));
5715
5716                 for (i=0; i < (ssize_t) (length/3); i++)
5717                 {
5718                   mng_info->global_plte[i].red=p[3*i];
5719                   mng_info->global_plte[i].green=p[3*i+1];
5720                   mng_info->global_plte[i].blue=p[3*i+2];
5721                 }
5722
5723                 mng_info->global_plte_length=(unsigned int) (length/3);
5724               }
5725 #ifdef MNG_LOOSE
5726             for ( ; i < 256; i++)
5727             {
5728               mng_info->global_plte[i].red=i;
5729               mng_info->global_plte[i].green=i;
5730               mng_info->global_plte[i].blue=i;
5731             }
5732
5733             if (length != 0)
5734               mng_info->global_plte_length=256;
5735 #endif
5736             else
5737               mng_info->global_plte_length=0;
5738
5739             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5740             continue;
5741           }
5742
5743         if (memcmp(type,mng_tRNS,4) == 0)
5744           {
5745             /* read global tRNS */
5746
5747             if (length > 0 && length < 257)
5748               for (i=0; i < (ssize_t) length; i++)
5749                 mng_info->global_trns[i]=p[i];
5750
5751 #ifdef MNG_LOOSE
5752             for ( ; i < 256; i++)
5753               mng_info->global_trns[i]=255;
5754 #endif
5755             mng_info->global_trns_length=(unsigned int) length;
5756             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5757             continue;
5758           }
5759         if (memcmp(type,mng_gAMA,4) == 0)
5760           {
5761             if (length == 4)
5762               {
5763                 ssize_t
5764                   igamma;
5765
5766                 igamma=mng_get_long(p);
5767                 mng_info->global_gamma=((float) igamma)*0.00001;
5768                 mng_info->have_global_gama=MagickTrue;
5769               }
5770
5771             else
5772               mng_info->have_global_gama=MagickFalse;
5773
5774             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5775             continue;
5776           }
5777
5778         if (memcmp(type,mng_cHRM,4) == 0)
5779           {
5780             /* Read global cHRM */
5781
5782             if (length == 32)
5783               {
5784                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5785                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5786                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5787                 mng_info->global_chrm.red_primary.y=0.00001*
5788                   mng_get_long(&p[12]);
5789                 mng_info->global_chrm.green_primary.x=0.00001*
5790                   mng_get_long(&p[16]);
5791                 mng_info->global_chrm.green_primary.y=0.00001*
5792                   mng_get_long(&p[20]);
5793                 mng_info->global_chrm.blue_primary.x=0.00001*
5794                   mng_get_long(&p[24]);
5795                 mng_info->global_chrm.blue_primary.y=0.00001*
5796                   mng_get_long(&p[28]);
5797                 mng_info->have_global_chrm=MagickTrue;
5798               }
5799             else
5800               mng_info->have_global_chrm=MagickFalse;
5801
5802             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5803             continue;
5804           }
5805
5806         if (memcmp(type,mng_sRGB,4) == 0)
5807           {
5808             /*
5809               Read global sRGB.
5810             */
5811             if (length != 0)
5812               {
5813                 mng_info->global_srgb_intent=
5814                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5815                 mng_info->have_global_srgb=MagickTrue;
5816               }
5817             else
5818               mng_info->have_global_srgb=MagickFalse;
5819
5820             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5821             continue;
5822           }
5823
5824         if (memcmp(type,mng_iCCP,4) == 0)
5825           {
5826             /* To do: */
5827
5828             /*
5829               Read global iCCP.
5830             */
5831             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5832
5833             continue;
5834           }
5835
5836         if (memcmp(type,mng_FRAM,4) == 0)
5837           {
5838             if (mng_type == 3)
5839               (void) ThrowMagickException(exception,GetMagickModule(),
5840                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5841                 image->filename);
5842
5843             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5844               image->delay=frame_delay;
5845
5846             frame_delay=default_frame_delay;
5847             frame_timeout=default_frame_timeout;
5848             fb=default_fb;
5849
5850             if (length != 0)
5851               if (p[0])
5852                 mng_info->framing_mode=p[0];
5853
5854             if (logging != MagickFalse)
5855               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5856                 "    Framing_mode=%d",mng_info->framing_mode);
5857
5858             if (length > 6)
5859               {
5860                 /* Note the delay and frame clipping boundaries.  */
5861
5862                 p++; /* framing mode */
5863
5864                 while (*p && ((p-chunk) < (ssize_t) length))
5865                   p++;  /* frame name */
5866
5867                 p++;  /* frame name terminator */
5868
5869                 if ((p-chunk) < (ssize_t) (length-4))
5870                   {
5871                     int
5872                       change_delay,
5873                       change_timeout,
5874                       change_clipping;
5875
5876                     change_delay=(*p++);
5877                     change_timeout=(*p++);
5878                     change_clipping=(*p++);
5879                     p++; /* change_sync */
5880
5881                     if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5882                       {
5883                         frame_delay=1UL*image->ticks_per_second*
5884                           mng_get_long(p);
5885
5886                         if (mng_info->ticks_per_second != 0)
5887                           frame_delay/=mng_info->ticks_per_second;
5888
5889                         else
5890                           frame_delay=PNG_UINT_31_MAX;
5891
5892                         if (change_delay == 2)
5893                           default_frame_delay=frame_delay;
5894
5895                         p+=4;
5896
5897                         if (logging != MagickFalse)
5898                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5899                             "    Framing_delay=%.20g",(double) frame_delay);
5900                       }
5901
5902                     if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
5903                       {
5904                         frame_timeout=1UL*image->ticks_per_second*
5905                           mng_get_long(p);
5906
5907                         if (mng_info->ticks_per_second != 0)
5908                           frame_timeout/=mng_info->ticks_per_second;
5909
5910                         else
5911                           frame_timeout=PNG_UINT_31_MAX;
5912
5913                         if (change_timeout == 2)
5914                           default_frame_timeout=frame_timeout;
5915
5916                         p+=4;
5917
5918                         if (logging != MagickFalse)
5919                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5920                             "    Framing_timeout=%.20g",(double) frame_timeout);
5921                       }
5922
5923                     if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
5924                       {
5925                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5926                         p+=16;
5927                         previous_fb=fb;
5928
5929                         if (logging != MagickFalse)
5930                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5931                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5932                             (double) fb.left,(double) fb.right,(double) fb.top,
5933                             (double) fb.bottom);
5934
5935                         if (change_clipping == 2)
5936                           default_fb=fb;
5937                       }
5938                   }
5939               }
5940             mng_info->clip=fb;
5941             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5942
5943             subframe_width=(size_t) (mng_info->clip.right
5944                -mng_info->clip.left);
5945
5946             subframe_height=(size_t) (mng_info->clip.bottom
5947                -mng_info->clip.top);
5948             /*
5949               Insert a background layer behind the frame if framing_mode is 4.
5950             */
5951 #if defined(MNG_INSERT_LAYERS)
5952             if (logging != MagickFalse)
5953               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5954                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5955                 subframe_width,(double) subframe_height);
5956
5957             if (insert_layers && (mng_info->framing_mode == 4) &&
5958                 (subframe_width) && (subframe_height))
5959               {
5960                 /* Allocate next image structure.  */
5961                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5962                   {
5963                     AcquireNextImage(image_info,image,exception);
5964
5965                     if (GetNextImageInList(image) == (Image *) NULL)
5966                       return(DestroyImageList(image));
5967
5968                     image=SyncNextImageInList(image);
5969                   }
5970
5971                 mng_info->image=image;
5972
5973                 if (term_chunk_found)
5974                   {
5975                     image->start_loop=MagickTrue;
5976                     image->iterations=mng_iterations;
5977                     term_chunk_found=MagickFalse;
5978                   }
5979
5980                 else
5981                     image->start_loop=MagickFalse;
5982
5983                 image->columns=subframe_width;
5984                 image->rows=subframe_height;
5985                 image->page.width=subframe_width;
5986                 image->page.height=subframe_height;
5987                 image->page.x=mng_info->clip.left;
5988                 image->page.y=mng_info->clip.top;
5989                 image->background_color=mng_background_color;
5990                 image->alpha_trait=UndefinedPixelTrait;
5991                 image->delay=0;
5992                 (void) SetImageBackgroundColor(image,exception);
5993
5994                 if (logging != MagickFalse)
5995                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5996                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5997                     (double) mng_info->clip.left,
5998                     (double) mng_info->clip.right,
5999                     (double) mng_info->clip.top,
6000                     (double) mng_info->clip.bottom);
6001               }
6002 #endif
6003             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6004             continue;
6005           }
6006
6007         if (memcmp(type,mng_CLIP,4) == 0)
6008           {
6009             unsigned int
6010               first_object,
6011               last_object;
6012
6013             /*
6014               Read CLIP.
6015             */
6016             if (length > 3)
6017               {
6018                 first_object=(p[0] << 8) | p[1];
6019                 last_object=(p[2] << 8) | p[3];
6020                 p+=4;
6021
6022                 for (i=(int) first_object; i <= (int) last_object; i++)
6023                 {
6024                   if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6025                     continue;
6026
6027                   if (mng_info->exists[i] && !mng_info->frozen[i])
6028                     {
6029                       MngBox
6030                         box;
6031
6032                       box=mng_info->object_clip[i];
6033                       if ((p-chunk) < (ssize_t) (length-17))
6034                         mng_info->object_clip[i]=
6035                            mng_read_box(box,(char) p[0],&p[1]);
6036                     }
6037                 }
6038
6039               }
6040             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6041             continue;
6042           }
6043
6044         if (memcmp(type,mng_SAVE,4) == 0)
6045           {
6046             for (i=1; i < MNG_MAX_OBJECTS; i++)
6047               if (mng_info->exists[i])
6048                 {
6049                  mng_info->frozen[i]=MagickTrue;
6050 #ifdef MNG_OBJECT_BUFFERS
6051                  if (mng_info->ob[i] != (MngBuffer *) NULL)
6052                     mng_info->ob[i]->frozen=MagickTrue;
6053 #endif
6054                 }
6055
6056             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6057
6058             continue;
6059           }
6060
6061         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
6062           {
6063             /* Read DISC or SEEK.  */
6064
6065             if ((length == 0) || !memcmp(type,mng_SEEK,4))
6066               {
6067                 for (i=1; i < MNG_MAX_OBJECTS; i++)
6068                   MngInfoDiscardObject(mng_info,i);
6069               }
6070
6071             else
6072               {
6073                 register ssize_t
6074                   j;
6075
6076                 for (j=1; j < (ssize_t) length; j+=2)
6077                 {
6078                   i=p[j-1] << 8 | p[j];
6079                   MngInfoDiscardObject(mng_info,i);
6080                 }
6081               }
6082
6083             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6084
6085             continue;
6086           }
6087
6088         if (memcmp(type,mng_MOVE,4) == 0)
6089           {
6090             size_t
6091               first_object,
6092               last_object;
6093
6094             /* read MOVE */
6095
6096             if (length > 3)
6097             {
6098               first_object=(p[0] << 8) | p[1];
6099               last_object=(p[2] << 8) | p[3];
6100               p+=4;
6101
6102               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
6103               {
6104                 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
6105                   continue;
6106
6107                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
6108                     (p-chunk) < (ssize_t) (length-8))
6109                   {
6110                     MngPair
6111                       new_pair;
6112
6113                     MngPair
6114                       old_pair;
6115
6116                     old_pair.a=mng_info->x_off[i];
6117                     old_pair.b=mng_info->y_off[i];
6118                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6119                     mng_info->x_off[i]=new_pair.a;
6120                     mng_info->y_off[i]=new_pair.b;
6121                   }
6122               }
6123             }
6124
6125             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6126             continue;
6127           }
6128
6129         if (memcmp(type,mng_LOOP,4) == 0)
6130           {
6131             ssize_t loop_iters=1;
6132             if (length > 4)
6133               {
6134                 loop_level=chunk[0];
6135                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
6136
6137                 /* Record starting point.  */
6138                 loop_iters=mng_get_long(&chunk[1]);
6139
6140                 if (logging != MagickFalse)
6141                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6142                     "  LOOP level %.20g has %.20g iterations ",
6143                     (double) loop_level, (double) loop_iters);
6144
6145                 if (loop_iters == 0)
6146                   skipping_loop=loop_level;
6147
6148                 else
6149                   {
6150                     mng_info->loop_jump[loop_level]=TellBlob(image);
6151                     mng_info->loop_count[loop_level]=loop_iters;
6152                   }
6153
6154                 mng_info->loop_iteration[loop_level]=0;
6155               }
6156             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6157             continue;
6158           }
6159
6160         if (memcmp(type,mng_ENDL,4) == 0)
6161           {
6162             if (length > 0)
6163               {
6164                 loop_level=chunk[0];
6165
6166                 if (skipping_loop > 0)
6167                   {
6168                     if (skipping_loop == loop_level)
6169                       {
6170                         /*
6171                           Found end of zero-iteration loop.
6172                         */
6173                         skipping_loop=(-1);
6174                         mng_info->loop_active[loop_level]=0;
6175                       }
6176                   }
6177
6178                 else
6179                   {
6180                     if (mng_info->loop_active[loop_level] == 1)
6181                       {
6182                         mng_info->loop_count[loop_level]--;
6183                         mng_info->loop_iteration[loop_level]++;
6184
6185                         if (logging != MagickFalse)
6186                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6187                           "  ENDL: LOOP level %.20g has %.20g remaining iters",
6188                             (double) loop_level,(double)
6189                             mng_info->loop_count[loop_level]);
6190
6191                         if (mng_info->loop_count[loop_level] != 0)
6192                           {
6193                             offset=
6194                               SeekBlob(image,mng_info->loop_jump[loop_level],
6195                               SEEK_SET);
6196
6197                             if (offset < 0)
6198                               {
6199                                 chunk=(unsigned char *) RelinquishMagickMemory(
6200                                   chunk);
6201                                 ThrowReaderException(CorruptImageError,
6202                                   "ImproperImageHeader");
6203                               }
6204                           }
6205
6206                         else
6207                           {
6208                             short
6209                               last_level;
6210
6211                             /*
6212                               Finished loop.
6213                             */
6214                             mng_info->loop_active[loop_level]=0;
6215                             last_level=(-1);
6216                             for (i=0; i < loop_level; i++)
6217                               if (mng_info->loop_active[i] == 1)
6218                                 last_level=(short) i;
6219                             loop_level=last_level;
6220                           }
6221                       }
6222                   }
6223               }
6224
6225             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6226             continue;
6227           }
6228
6229         if (memcmp(type,mng_CLON,4) == 0)
6230           {
6231             if (mng_info->clon_warning == 0)
6232               (void) ThrowMagickException(exception,GetMagickModule(),
6233                 CoderError,"CLON is not implemented yet","`%s'",
6234                 image->filename);
6235
6236             mng_info->clon_warning++;
6237           }
6238
6239         if (memcmp(type,mng_MAGN,4) == 0)
6240           {
6241             png_uint_16
6242               magn_first,
6243               magn_last,
6244               magn_mb,
6245               magn_ml,
6246               magn_mr,
6247               magn_mt,
6248               magn_mx,
6249               magn_my,
6250               magn_methx,
6251               magn_methy;
6252
6253             if (length > 1)
6254               magn_first=(p[0] << 8) | p[1];
6255
6256             else
6257               magn_first=0;
6258
6259             if (length > 3)
6260               magn_last=(p[2] << 8) | p[3];
6261
6262             else
6263               magn_last=magn_first;
6264 #ifndef MNG_OBJECT_BUFFERS
6265             if (magn_first || magn_last)
6266               if (mng_info->magn_warning == 0)
6267                 {
6268                   (void) ThrowMagickException(exception,
6269                      GetMagickModule(),CoderError,
6270                      "MAGN is not implemented yet for nonzero objects",
6271                      "`%s'",image->filename);
6272
6273                    mng_info->magn_warning++;
6274                 }
6275 #endif
6276             if (length > 4)
6277               magn_methx=p[4];
6278
6279             else
6280               magn_methx=0;
6281
6282             if (length > 6)
6283               magn_mx=(p[5] << 8) | p[6];
6284
6285             else
6286               magn_mx=1;
6287
6288             if (magn_mx == 0)
6289               magn_mx=1;
6290
6291             if (length > 8)
6292               magn_my=(p[7] << 8) | p[8];
6293
6294             else
6295               magn_my=magn_mx;
6296
6297             if (magn_my == 0)
6298               magn_my=1;
6299
6300             if (length > 10)
6301               magn_ml=(p[9] << 8) | p[10];
6302
6303             else
6304               magn_ml=magn_mx;
6305
6306             if (magn_ml == 0)
6307               magn_ml=1;
6308
6309             if (length > 12)
6310               magn_mr=(p[11] << 8) | p[12];
6311
6312             else
6313               magn_mr=magn_mx;
6314
6315             if (magn_mr == 0)
6316               magn_mr=1;
6317
6318             if (length > 14)
6319               magn_mt=(p[13] << 8) | p[14];
6320
6321             else
6322               magn_mt=magn_my;
6323
6324             if (magn_mt == 0)
6325               magn_mt=1;
6326
6327             if (length > 16)
6328               magn_mb=(p[15] << 8) | p[16];
6329
6330             else
6331               magn_mb=magn_my;
6332
6333             if (magn_mb == 0)
6334               magn_mb=1;
6335
6336             if (length > 17)
6337               magn_methy=p[17];
6338
6339             else
6340               magn_methy=magn_methx;
6341
6342
6343             if (magn_methx > 5 || magn_methy > 5)
6344               if (mng_info->magn_warning == 0)
6345                 {
6346                   (void) ThrowMagickException(exception,
6347                      GetMagickModule(),CoderError,
6348                      "Unknown MAGN method in MNG datastream","`%s'",
6349                      image->filename);
6350
6351                    mng_info->magn_warning++;
6352                 }
6353 #ifdef MNG_OBJECT_BUFFERS
6354           /* Magnify existing objects in the range magn_first to magn_last */
6355 #endif
6356             if (magn_first == 0 || magn_last == 0)
6357               {
6358                 /* Save the magnification factors for object 0 */
6359                 mng_info->magn_mb=magn_mb;
6360                 mng_info->magn_ml=magn_ml;
6361                 mng_info->magn_mr=magn_mr;
6362                 mng_info->magn_mt=magn_mt;
6363                 mng_info->magn_mx=magn_mx;
6364                 mng_info->magn_my=magn_my;
6365                 mng_info->magn_methx=magn_methx;
6366                 mng_info->magn_methy=magn_methy;
6367               }
6368           }
6369
6370         if (memcmp(type,mng_PAST,4) == 0)
6371           {
6372             if (mng_info->past_warning == 0)
6373               (void) ThrowMagickException(exception,GetMagickModule(),
6374                 CoderError,"PAST is not implemented yet","`%s'",
6375                 image->filename);
6376
6377             mng_info->past_warning++;
6378           }
6379
6380         if (memcmp(type,mng_SHOW,4) == 0)
6381           {
6382             if (mng_info->show_warning == 0)
6383               (void) ThrowMagickException(exception,GetMagickModule(),
6384                 CoderError,"SHOW is not implemented yet","`%s'",
6385                 image->filename);
6386
6387             mng_info->show_warning++;
6388           }
6389
6390         if (memcmp(type,mng_sBIT,4) == 0)
6391           {
6392             if (length < 4)
6393               mng_info->have_global_sbit=MagickFalse;
6394
6395             else
6396               {
6397                 mng_info->global_sbit.gray=p[0];
6398                 mng_info->global_sbit.red=p[0];
6399                 mng_info->global_sbit.green=p[1];
6400                 mng_info->global_sbit.blue=p[2];
6401                 mng_info->global_sbit.alpha=p[3];
6402                 mng_info->have_global_sbit=MagickTrue;
6403              }
6404           }
6405         if (memcmp(type,mng_pHYs,4) == 0)
6406           {
6407             if (length > 8)
6408               {
6409                 mng_info->global_x_pixels_per_unit=
6410                     (size_t) mng_get_long(p);
6411                 mng_info->global_y_pixels_per_unit=
6412                     (size_t) mng_get_long(&p[4]);
6413                 mng_info->global_phys_unit_type=p[8];
6414                 mng_info->have_global_phys=MagickTrue;
6415               }
6416
6417             else
6418               mng_info->have_global_phys=MagickFalse;
6419           }
6420         if (memcmp(type,mng_pHYg,4) == 0)
6421           {
6422             if (mng_info->phyg_warning == 0)
6423               (void) ThrowMagickException(exception,GetMagickModule(),
6424                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6425
6426             mng_info->phyg_warning++;
6427           }
6428         if (memcmp(type,mng_BASI,4) == 0)
6429           {
6430             skip_to_iend=MagickTrue;
6431
6432             if (mng_info->basi_warning == 0)
6433               (void) ThrowMagickException(exception,GetMagickModule(),
6434                 CoderError,"BASI is not implemented yet","`%s'",
6435                 image->filename);
6436
6437             mng_info->basi_warning++;
6438 #ifdef MNG_BASI_SUPPORTED
6439             basi_width=(unsigned long) mng_get_long(p);
6440             basi_width=(unsigned long) mng_get_long(&p[4]);
6441             basi_color_type=p[8];
6442             basi_compression_method=p[9];
6443             basi_filter_type=p[10];
6444             basi_interlace_method=p[11];
6445             if (length > 11)
6446               basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6447
6448             else
6449               basi_red=0;
6450
6451             if (length > 13)
6452               basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6453
6454             else
6455               basi_green=0;
6456
6457             if (length > 15)
6458               basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6459
6460             else
6461               basi_blue=0;
6462
6463             if (length > 17)
6464               basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6465
6466             else
6467               {
6468                 if (basi_sample_depth == 16)
6469                   basi_alpha=65535L;
6470                 else
6471                   basi_alpha=255;
6472               }
6473
6474             if (length > 19)
6475               basi_viewable=p[20];
6476
6477             else
6478               basi_viewable=0;
6479
6480 #endif
6481             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6482             continue;
6483           }
6484
6485         if (memcmp(type,mng_IHDR,4)
6486 #if defined(JNG_SUPPORTED)
6487             && memcmp(type,mng_JHDR,4)
6488 #endif
6489             )
6490           {
6491             /* Not an IHDR or JHDR chunk */
6492             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6493
6494             continue;
6495           }
6496 /* Process IHDR */
6497         if (logging != MagickFalse)
6498           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6499             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6500
6501         mng_info->exists[object_id]=MagickTrue;
6502         mng_info->viewable[object_id]=MagickTrue;
6503
6504         if (mng_info->invisible[object_id])
6505           {
6506             if (logging != MagickFalse)
6507               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6508                 "  Skipping invisible object");
6509
6510             skip_to_iend=MagickTrue;
6511             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6512             continue;
6513           }
6514 #if defined(MNG_INSERT_LAYERS)
6515         if (length < 8)
6516           {
6517             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6518             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6519           }
6520
6521         image_width=(size_t) mng_get_long(p);
6522         image_height=(size_t) mng_get_long(&p[4]);
6523 #endif
6524         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6525
6526         /*
6527           Insert a transparent background layer behind the entire animation
6528           if it is not full screen.
6529         */
6530 #if defined(MNG_INSERT_LAYERS)
6531         if (insert_layers && mng_type && first_mng_object)
6532           {
6533             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6534                 (image_width < mng_info->mng_width) ||
6535                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6536                 (image_height < mng_info->mng_height) ||
6537                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6538               {
6539                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6540                   {
6541                     /*
6542                       Allocate next image structure.
6543                     */
6544                     AcquireNextImage(image_info,image,exception);
6545
6546                     if (GetNextImageInList(image) == (Image *) NULL)
6547                       return(DestroyImageList(image));
6548
6549                     image=SyncNextImageInList(image);
6550                   }
6551                 mng_info->image=image;
6552
6553                 if (term_chunk_found)
6554                   {
6555                     image->start_loop=MagickTrue;
6556                     image->iterations=mng_iterations;
6557                     term_chunk_found=MagickFalse;
6558                   }
6559
6560                 else
6561                     image->start_loop=MagickFalse;
6562
6563                 /* Make a background rectangle.  */
6564
6565                 image->delay=0;
6566                 image->columns=mng_info->mng_width;
6567                 image->rows=mng_info->mng_height;
6568                 image->page.width=mng_info->mng_width;
6569                 image->page.height=mng_info->mng_height;
6570                 image->page.x=0;
6571                 image->page.y=0;
6572                 image->background_color=mng_background_color;
6573                 (void) SetImageBackgroundColor(image,exception);
6574                 if (logging != MagickFalse)
6575                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6576                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6577                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6578               }
6579           }
6580         /*
6581           Insert a background layer behind the upcoming image if
6582           framing_mode is 3, and we haven't already inserted one.
6583         */
6584         if (insert_layers && (mng_info->framing_mode == 3) &&
6585                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6586                 (simplicity & 0x08)))
6587           {
6588             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6589             {
6590               /*
6591                 Allocate next image structure.
6592               */
6593               AcquireNextImage(image_info,image,exception);
6594
6595               if (GetNextImageInList(image) == (Image *) NULL)
6596                 return(DestroyImageList(image));
6597
6598               image=SyncNextImageInList(image);
6599             }
6600
6601             mng_info->image=image;
6602
6603             if (term_chunk_found)
6604               {
6605                 image->start_loop=MagickTrue;
6606                 image->iterations=mng_iterations;
6607                 term_chunk_found=MagickFalse;
6608               }
6609
6610             else
6611                 image->start_loop=MagickFalse;
6612
6613             image->delay=0;
6614             image->columns=subframe_width;
6615             image->rows=subframe_height;
6616             image->page.width=subframe_width;
6617             image->page.height=subframe_height;
6618             image->page.x=mng_info->clip.left;
6619             image->page.y=mng_info->clip.top;
6620             image->background_color=mng_background_color;
6621             image->alpha_trait=UndefinedPixelTrait;
6622             (void) SetImageBackgroundColor(image,exception);
6623
6624             if (logging != MagickFalse)
6625               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6626                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6627                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6628                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6629           }
6630 #endif /* MNG_INSERT_LAYERS */
6631         first_mng_object=MagickFalse;
6632
6633         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6634           {
6635             /*
6636               Allocate next image structure.
6637             */
6638             AcquireNextImage(image_info,image,exception);
6639
6640             if (GetNextImageInList(image) == (Image *) NULL)
6641               return(DestroyImageList(image));
6642
6643             image=SyncNextImageInList(image);
6644           }
6645         mng_info->image=image;
6646         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6647           GetBlobSize(image));
6648
6649         if (status == MagickFalse)
6650           break;
6651
6652         if (term_chunk_found)
6653           {
6654             image->start_loop=MagickTrue;
6655             term_chunk_found=MagickFalse;
6656           }
6657
6658         else
6659             image->start_loop=MagickFalse;
6660
6661         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6662           {
6663             image->delay=frame_delay;
6664             frame_delay=default_frame_delay;
6665           }
6666
6667         else
6668           image->delay=0;
6669
6670         image->page.width=mng_info->mng_width;
6671         image->page.height=mng_info->mng_height;
6672         image->page.x=mng_info->x_off[object_id];
6673         image->page.y=mng_info->y_off[object_id];
6674         image->iterations=mng_iterations;
6675
6676         /*
6677           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6678         */
6679
6680         if (logging != MagickFalse)
6681           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6682             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6683             type[2],type[3]);
6684
6685         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6686
6687         if (offset < 0)
6688           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6689       }
6690
6691     mng_info->image=image;
6692     mng_info->mng_type=mng_type;
6693     mng_info->object_id=object_id;
6694
6695     if (memcmp(type,mng_IHDR,4) == 0)
6696       image=ReadOnePNGImage(mng_info,image_info,exception);
6697
6698 #if defined(JNG_SUPPORTED)
6699     else
6700       image=ReadOneJNGImage(mng_info,image_info,exception);
6701 #endif
6702
6703     if (image == (Image *) NULL)
6704       {
6705         if (logging != MagickFalse)
6706           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6707             "exit ReadJNGImage() with error");
6708
6709         return((Image *) NULL);
6710       }
6711
6712     if (image->columns == 0 || image->rows == 0)
6713       {
6714         (void) CloseBlob(image);
6715         return(DestroyImageList(image));
6716       }
6717
6718     mng_info->image=image;
6719
6720     if (mng_type)
6721       {
6722         MngBox
6723           crop_box;
6724
6725         if (mng_info->magn_methx || mng_info->magn_methy)
6726           {
6727             png_uint_32
6728                magnified_height,
6729                magnified_width;
6730
6731             if (logging != MagickFalse)
6732               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6733                 "  Processing MNG MAGN chunk");
6734
6735             if (mng_info->magn_methx == 1)
6736               {
6737                 magnified_width=mng_info->magn_ml;
6738
6739                 if (image->columns > 1)
6740                    magnified_width += mng_info->magn_mr;
6741
6742                 if (image->columns > 2)
6743                    magnified_width += (png_uint_32)
6744                       ((image->columns-2)*(mng_info->magn_mx));
6745               }
6746
6747             else
6748               {
6749                 magnified_width=(png_uint_32) image->columns;
6750
6751                 if (image->columns > 1)
6752                    magnified_width += mng_info->magn_ml-1;
6753
6754                 if (image->columns > 2)
6755                    magnified_width += mng_info->magn_mr-1;
6756
6757                 if (image->columns > 3)
6758                    magnified_width += (png_uint_32)
6759                       ((image->columns-3)*(mng_info->magn_mx-1));
6760               }
6761
6762             if (mng_info->magn_methy == 1)
6763               {
6764                 magnified_height=mng_info->magn_mt;
6765
6766                 if (image->rows > 1)
6767                    magnified_height += mng_info->magn_mb;
6768
6769                 if (image->rows > 2)
6770                    magnified_height += (png_uint_32)
6771                       ((image->rows-2)*(mng_info->magn_my));
6772               }
6773
6774             else
6775               {
6776                 magnified_height=(png_uint_32) image->rows;
6777
6778                 if (image->rows > 1)
6779                    magnified_height += mng_info->magn_mt-1;
6780
6781                 if (image->rows > 2)
6782                    magnified_height += mng_info->magn_mb-1;
6783
6784                 if (image->rows > 3)
6785                    magnified_height += (png_uint_32)
6786                       ((image->rows-3)*(mng_info->magn_my-1));
6787               }
6788
6789             if (magnified_height > image->rows ||
6790                 magnified_width > image->columns)
6791               {
6792                 Image
6793                   *large_image;
6794
6795                 int
6796                   yy;
6797
6798                 Quantum
6799                   *next,
6800                   *prev;
6801
6802                 png_uint_16
6803                   magn_methx,
6804                   magn_methy;
6805
6806                 ssize_t
6807                   m,
6808                   y;
6809
6810                 register Quantum
6811                   *n,
6812                   *q;
6813
6814                 register ssize_t
6815                   x;
6816
6817                 /* Allocate next image structure.  */
6818
6819                 if (logging != MagickFalse)
6820                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6821                     "    Allocate magnified image");
6822
6823                 AcquireNextImage(image_info,image,exception);
6824
6825                 if (GetNextImageInList(image) == (Image *) NULL)
6826                   return(DestroyImageList(image));
6827
6828                 large_image=SyncNextImageInList(image);
6829
6830                 large_image->columns=magnified_width;
6831                 large_image->rows=magnified_height;
6832
6833                 magn_methx=mng_info->magn_methx;
6834                 magn_methy=mng_info->magn_methy;
6835
6836 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6837 #define QM unsigned short
6838                 if (magn_methx != 1 || magn_methy != 1)
6839                   {
6840                   /*
6841                      Scale pixels to unsigned shorts to prevent
6842                      overflow of intermediate values of interpolations
6843                   */
6844                      for (y=0; y < (ssize_t) image->rows; y++)
6845                      {
6846                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6847                           exception);
6848
6849                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6850                        {
6851                           SetPixelRed(image,ScaleQuantumToShort(
6852                             GetPixelRed(image,q)),q);
6853                           SetPixelGreen(image,ScaleQuantumToShort(
6854                             GetPixelGreen(image,q)),q);
6855                           SetPixelBlue(image,ScaleQuantumToShort(
6856                             GetPixelBlue(image,q)),q);
6857                           SetPixelAlpha(image,ScaleQuantumToShort(
6858                             GetPixelAlpha(image,q)),q);
6859                           q+=GetPixelChannels(image);
6860                        }
6861
6862                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6863                          break;
6864                      }
6865                   }
6866 #else
6867 #define QM Quantum
6868 #endif
6869
6870                 if (image->alpha_trait != UndefinedPixelTrait)
6871                    (void) SetImageBackgroundColor(large_image,exception);
6872
6873                 else
6874                   {
6875                     large_image->background_color.alpha=OpaqueAlpha;
6876                     (void) SetImageBackgroundColor(large_image,exception);
6877
6878                     if (magn_methx == 4)
6879                       magn_methx=2;
6880
6881                     if (magn_methx == 5)
6882                       magn_methx=3;
6883
6884                     if (magn_methy == 4)
6885                       magn_methy=2;
6886
6887                     if (magn_methy == 5)
6888                       magn_methy=3;
6889                   }
6890
6891                 /* magnify the rows into the right side of the large image */
6892
6893                 if (logging != MagickFalse)
6894                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6895                     "    Magnify the rows to %.20g",
6896                     (double) large_image->rows);
6897                 m=(ssize_t) mng_info->magn_mt;
6898                 yy=0;
6899                 length=(size_t) GetPixelChannels(image)*image->columns;
6900                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6901                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6902
6903                 if ((prev == (Quantum *) NULL) ||
6904                     (next == (Quantum *) NULL))
6905                   {
6906                      image=DestroyImageList(image);
6907                      ThrowReaderException(ResourceLimitError,
6908                        "MemoryAllocationFailed");
6909                   }
6910
6911                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6912                 (void) CopyMagickMemory(next,n,length);
6913
6914                 for (y=0; y < (ssize_t) image->rows; y++)
6915                 {
6916                   if (y == 0)
6917                     m=(ssize_t) mng_info->magn_mt;
6918
6919                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6920                     m=(ssize_t) mng_info->magn_mb;
6921
6922                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6923                     m=(ssize_t) mng_info->magn_mb;
6924
6925                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6926                     m=1;
6927
6928                   else
6929                     m=(ssize_t) mng_info->magn_my;
6930
6931                   n=prev;
6932                   prev=next;
6933                   next=n;
6934
6935                   if (y < (ssize_t) image->rows-1)
6936                     {
6937                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6938                           exception);
6939                       (void) CopyMagickMemory(next,n,length);
6940                     }
6941
6942                   for (i=0; i < m; i++, yy++)
6943                   {
6944                     register Quantum
6945                       *pixels;
6946
6947                     assert(yy < (ssize_t) large_image->rows);
6948                     pixels=prev;
6949                     n=next;
6950                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6951                       1,exception);
6952                     q+=(large_image->columns-image->columns)*
6953                       GetPixelChannels(large_image);
6954
6955                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6956                     {
6957                       /* To do: get color as function of indexes[x] */
6958                       /*
6959                       if (image->storage_class == PseudoClass)
6960                         {
6961                         }
6962                       */
6963
6964                       if (magn_methy <= 1)
6965                         {
6966                           /* replicate previous */
6967                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6968                           SetPixelGreen(large_image,GetPixelGreen(image,
6969                              pixels),q);
6970                           SetPixelBlue(large_image,GetPixelBlue(image,
6971                              pixels),q);
6972                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6973                              pixels),q);
6974                         }
6975
6976                       else if (magn_methy == 2 || magn_methy == 4)
6977                         {
6978                           if (i == 0)
6979                             {
6980                               SetPixelRed(large_image,GetPixelRed(image,
6981                                  pixels),q);
6982                               SetPixelGreen(large_image,GetPixelGreen(image,
6983                                  pixels),q);
6984                               SetPixelBlue(large_image,GetPixelBlue(image,
6985                                  pixels),q);
6986                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6987                                  pixels),q);
6988                             }
6989
6990                           else
6991                             {
6992                               /* Interpolate */
6993                               SetPixelRed(large_image,((QM) (((ssize_t)
6994                                  (2*i*(GetPixelRed(image,n)
6995                                  -GetPixelRed(image,pixels)+m))/
6996                                  ((ssize_t) (m*2))
6997                                  +GetPixelRed(image,pixels)))),q);
6998                               SetPixelGreen(large_image,((QM) (((ssize_t)
6999                                  (2*i*(GetPixelGreen(image,n)
7000                                  -GetPixelGreen(image,pixels)+m))/
7001                                  ((ssize_t) (m*2))
7002                                  +GetPixelGreen(image,pixels)))),q);
7003                               SetPixelBlue(large_image,((QM) (((ssize_t)
7004                                  (2*i*(GetPixelBlue(image,n)
7005                                  -GetPixelBlue(image,pixels)+m))/
7006                                  ((ssize_t) (m*2))
7007                                  +GetPixelBlue(image,pixels)))),q);
7008
7009                               if (image->alpha_trait != UndefinedPixelTrait)
7010                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
7011                                     (2*i*(GetPixelAlpha(image,n)
7012                                     -GetPixelAlpha(image,pixels)+m))
7013                                     /((ssize_t) (m*2))+
7014                                    GetPixelAlpha(image,pixels)))),q);
7015                             }
7016
7017                           if (magn_methy == 4)
7018                             {
7019                               /* Replicate nearest */
7020                               if (i <= ((m+1) << 1))
7021                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7022                                     pixels),q);
7023                               else
7024                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
7025                                     n),q);
7026                             }
7027                         }
7028
7029                       else /* if (magn_methy == 3 || magn_methy == 5) */
7030                         {
7031                           /* Replicate nearest */
7032                           if (i <= ((m+1) << 1))
7033                           {
7034                              SetPixelRed(large_image,GetPixelRed(image,
7035                                     pixels),q);
7036                              SetPixelGreen(large_image,GetPixelGreen(image,
7037                                     pixels),q);
7038                              SetPixelBlue(large_image,GetPixelBlue(image,
7039                                     pixels),q);
7040                              SetPixelAlpha(large_image,GetPixelAlpha(image,
7041                                     pixels),q);
7042                           }
7043
7044                           else
7045                           {
7046                              SetPixelRed(large_image,GetPixelRed(image,n),q);
7047                              SetPixelGreen(large_image,GetPixelGreen(image,n),
7048                                     q);
7049                              SetPixelBlue(large_image,GetPixelBlue(image,n),
7050                                     q);
7051                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
7052                                     q);
7053                           }
7054
7055                           if (magn_methy == 5)
7056                             {
7057                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
7058                                  (GetPixelAlpha(image,n)
7059                                  -GetPixelAlpha(image,pixels))
7060                                  +m))/((ssize_t) (m*2))
7061                                  +GetPixelAlpha(image,pixels)),q);
7062                             }
7063                         }
7064                       n+=GetPixelChannels(image);
7065                       q+=GetPixelChannels(large_image);
7066                       pixels+=GetPixelChannels(image);
7067                     } /* x */
7068
7069                     if (SyncAuthenticPixels(large_image,exception) == 0)
7070                       break;
7071
7072                   } /* i */
7073                 } /* y */
7074
7075                 prev=(Quantum *) RelinquishMagickMemory(prev);
7076                 next=(Quantum *) RelinquishMagickMemory(next);
7077
7078                 length=image->columns;
7079
7080                 if (logging != MagickFalse)
7081                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7082                     "    Delete original image");
7083
7084                 DeleteImageFromList(&image);
7085
7086                 image=large_image;
7087
7088                 mng_info->image=image;
7089
7090                 /* magnify the columns */
7091                 if (logging != MagickFalse)
7092                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7093                     "    Magnify the columns to %.20g",
7094                     (double) image->columns);
7095
7096                 for (y=0; y < (ssize_t) image->rows; y++)
7097                 {
7098                   register Quantum
7099                     *pixels;
7100
7101                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
7102                   pixels=q+(image->columns-length)*GetPixelChannels(image);
7103                   n=pixels+GetPixelChannels(image);
7104
7105                   for (x=(ssize_t) (image->columns-length);
7106                     x < (ssize_t) image->columns; x++)
7107                   {
7108                     /* To do: Rewrite using Get/Set***PixelChannel() */
7109
7110                     if (x == (ssize_t) (image->columns-length))
7111                       m=(ssize_t) mng_info->magn_ml;
7112
7113                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7114                       m=(ssize_t) mng_info->magn_mr;
7115
7116                     else if (magn_methx <= 1 &&
7117                         x == (ssize_t) image->columns-1)
7118                       m=(ssize_t) mng_info->magn_mr;
7119
7120                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7121                       m=1;
7122
7123                     else
7124                       m=(ssize_t) mng_info->magn_mx;
7125
7126                     for (i=0; i < m; i++)
7127                     {
7128                       if (magn_methx <= 1)
7129                         {
7130                           /* replicate previous */
7131                           SetPixelRed(image,GetPixelRed(image,pixels),q);
7132                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7133                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7134                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7135                         }
7136
7137                       else if (magn_methx == 2 || magn_methx == 4)
7138                         {
7139                           if (i == 0)
7140                           {
7141                             SetPixelRed(image,GetPixelRed(image,pixels),q);
7142                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7143                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7144                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7145                           }
7146
7147                           /* To do: Rewrite using Get/Set***PixelChannel() */
7148                           else
7149                             {
7150                               /* Interpolate */
7151                               SetPixelRed(image,(QM) ((2*i*(
7152                                  GetPixelRed(image,n)
7153                                  -GetPixelRed(image,pixels))+m)
7154                                  /((ssize_t) (m*2))+
7155                                  GetPixelRed(image,pixels)),q);
7156
7157                               SetPixelGreen(image,(QM) ((2*i*(
7158                                  GetPixelGreen(image,n)
7159                                  -GetPixelGreen(image,pixels))+m)
7160                                  /((ssize_t) (m*2))+
7161                                  GetPixelGreen(image,pixels)),q);
7162
7163                               SetPixelBlue(image,(QM) ((2*i*(
7164                                  GetPixelBlue(image,n)
7165                                  -GetPixelBlue(image,pixels))+m)
7166                                  /((ssize_t) (m*2))+
7167                                  GetPixelBlue(image,pixels)),q);
7168                               if (image->alpha_trait != UndefinedPixelTrait)
7169                                  SetPixelAlpha(image,(QM) ((2*i*(
7170                                    GetPixelAlpha(image,n)
7171                                    -GetPixelAlpha(image,pixels))+m)
7172                                    /((ssize_t) (m*2))+
7173                                    GetPixelAlpha(image,pixels)),q);
7174                             }
7175
7176                           if (magn_methx == 4)
7177                             {
7178                               /* Replicate nearest */
7179                               if (i <= ((m+1) << 1))
7180                               {
7181                                  SetPixelAlpha(image,
7182                                    GetPixelAlpha(image,pixels)+0,q);
7183                               }
7184                               else
7185                               {
7186                                  SetPixelAlpha(image,
7187                                    GetPixelAlpha(image,n)+0,q);
7188                               }
7189                             }
7190                         }
7191
7192                       else /* if (magn_methx == 3 || magn_methx == 5) */
7193                         {
7194                           /* Replicate nearest */
7195                           if (i <= ((m+1) << 1))
7196                           {
7197                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7198                              SetPixelGreen(image,GetPixelGreen(image,
7199                                  pixels),q);
7200                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7201                              SetPixelAlpha(image,GetPixelAlpha(image,
7202                                  pixels),q);
7203                           }
7204
7205                           else
7206                           {
7207                              SetPixelRed(image,GetPixelRed(image,n),q);
7208                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7209                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7210                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7211                           }
7212
7213                           if (magn_methx == 5)
7214                             {
7215                               /* Interpolate */
7216                               SetPixelAlpha(image,
7217                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7218                                  -GetPixelAlpha(image,pixels))+m)/
7219                                  ((ssize_t) (m*2))
7220                                  +GetPixelAlpha(image,pixels)),q);
7221                             }
7222                         }
7223                       q+=GetPixelChannels(image);
7224                     }
7225                     n+=GetPixelChannels(image);
7226                   }
7227
7228                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7229                     break;
7230                 }
7231 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7232               if (magn_methx != 1 || magn_methy != 1)
7233                 {
7234                 /*
7235                    Rescale pixels to Quantum
7236                 */
7237                    for (y=0; y < (ssize_t) image->rows; y++)
7238                    {
7239                      q=GetAuthenticPixels(image,0,y,image->columns,1,
7240                        exception);
7241
7242                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7243                      {
7244                         SetPixelRed(image,ScaleShortToQuantum(
7245                           GetPixelRed(image,q)),q);
7246                         SetPixelGreen(image,ScaleShortToQuantum(
7247                           GetPixelGreen(image,q)),q);
7248                         SetPixelBlue(image,ScaleShortToQuantum(
7249                           GetPixelBlue(image,q)),q);
7250                         SetPixelAlpha(image,ScaleShortToQuantum(
7251                           GetPixelAlpha(image,q)),q);
7252                         q+=GetPixelChannels(image);
7253                      }
7254
7255                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7256                        break;
7257                    }
7258                 }
7259 #endif
7260                 if (logging != MagickFalse)
7261                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7262                     "  Finished MAGN processing");
7263               }
7264           }
7265
7266         /*
7267           Crop_box is with respect to the upper left corner of the MNG.
7268         */
7269         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7270         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7271         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7272         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7273         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7274         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7275         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7276         if ((crop_box.left != (mng_info->image_box.left
7277             +mng_info->x_off[object_id])) ||
7278             (crop_box.right != (mng_info->image_box.right
7279             +mng_info->x_off[object_id])) ||
7280             (crop_box.top != (mng_info->image_box.top
7281             +mng_info->y_off[object_id])) ||
7282             (crop_box.bottom != (mng_info->image_box.bottom
7283             +mng_info->y_off[object_id])))
7284           {
7285             if (logging != MagickFalse)
7286               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7287                 "  Crop the PNG image");
7288
7289             if ((crop_box.left < crop_box.right) &&
7290                 (crop_box.top < crop_box.bottom))
7291               {
7292                 Image
7293                   *im;
7294
7295                 RectangleInfo
7296                   crop_info;
7297
7298                 /*
7299                   Crop_info is with respect to the upper left corner of
7300                   the image.
7301                 */
7302                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7303                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7304                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7305                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7306                 image->page.width=image->columns;
7307                 image->page.height=image->rows;
7308                 image->page.x=0;
7309                 image->page.y=0;
7310                 im=CropImage(image,&crop_info,exception);
7311
7312                 if (im != (Image *) NULL)
7313                   {
7314                     image->columns=im->columns;
7315                     image->rows=im->rows;
7316                     im=DestroyImage(im);
7317                     image->page.width=image->columns;
7318                     image->page.height=image->rows;
7319                     image->page.x=crop_box.left;
7320                     image->page.y=crop_box.top;
7321                   }
7322               }
7323
7324             else
7325               {
7326                 /*
7327                   No pixels in crop area.  The MNG spec still requires
7328                   a layer, though, so make a single transparent pixel in
7329                   the top left corner.
7330                 */
7331                 image->columns=1;
7332                 image->rows=1;
7333                 image->colors=2;
7334                 (void) SetImageBackgroundColor(image,exception);
7335                 image->page.width=1;
7336                 image->page.height=1;
7337                 image->page.x=0;
7338                 image->page.y=0;
7339               }
7340           }
7341 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7342         image=mng_info->image;
7343 #endif
7344       }
7345
7346 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7347       /* PNG does not handle depths greater than 16 so reduce it even
7348        * if lossy.
7349        */
7350       if (image->depth > 16)
7351          image->depth=16;
7352 #endif
7353
7354 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7355       if (image->depth > 8)
7356         {
7357           /* To do: fill low byte properly */
7358           image->depth=16;
7359         }
7360
7361       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7362          image->depth = 8;
7363 #endif
7364
7365       if (image_info->number_scenes != 0)
7366         {
7367           if (mng_info->scenes_found >
7368              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7369             break;
7370         }
7371
7372       if (logging != MagickFalse)
7373         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7374           "  Finished reading image datastream.");
7375
7376   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7377
7378   (void) CloseBlob(image);
7379
7380   if (logging != MagickFalse)
7381     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7382       "  Finished reading all image datastreams.");
7383
7384 #if defined(MNG_INSERT_LAYERS)
7385   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7386        (mng_info->mng_height))
7387     {
7388       /*
7389         Insert a background layer if nothing else was found.
7390       */
7391       if (logging != MagickFalse)
7392         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7393           "  No images found.  Inserting a background layer.");
7394
7395       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7396         {
7397           /*
7398             Allocate next image structure.
7399           */
7400           AcquireNextImage(image_info,image,exception);
7401           if (GetNextImageInList(image) == (Image *) NULL)
7402             {
7403               if (logging != MagickFalse)
7404                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7405                   "  Allocation failed, returning NULL.");
7406
7407               return(DestroyImageList(image));;
7408             }
7409           image=SyncNextImageInList(image);
7410         }
7411       image->columns=mng_info->mng_width;
7412       image->rows=mng_info->mng_height;
7413       image->page.width=mng_info->mng_width;
7414       image->page.height=mng_info->mng_height;
7415       image->page.x=0;
7416       image->page.y=0;
7417       image->background_color=mng_background_color;
7418       image->alpha_trait=UndefinedPixelTrait;
7419
7420       if (image_info->ping == MagickFalse)
7421         (void) SetImageBackgroundColor(image,exception);
7422
7423       mng_info->image_found++;
7424     }
7425 #endif
7426   image->iterations=mng_iterations;
7427
7428   if (mng_iterations == 1)
7429     image->start_loop=MagickTrue;
7430
7431   while (GetPreviousImageInList(image) != (Image *) NULL)
7432   {
7433     image_count++;
7434     if (image_count > 10*mng_info->image_found)
7435       {
7436         if (logging != MagickFalse)
7437           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7438
7439         (void) ThrowMagickException(exception,GetMagickModule(),
7440           CoderError,"Linked list is corrupted, beginning of list not found",
7441           "`%s'",image_info->filename);
7442
7443         return(DestroyImageList(image));
7444       }
7445
7446     image=GetPreviousImageInList(image);
7447
7448     if (GetNextImageInList(image) == (Image *) NULL)
7449       {
7450         if (logging != MagickFalse)
7451           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7452
7453         (void) ThrowMagickException(exception,GetMagickModule(),
7454           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7455           image_info->filename);
7456       }
7457   }
7458
7459   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7460              GetNextImageInList(image) ==
7461      (Image *) NULL)
7462     {
7463       if (logging != MagickFalse)
7464         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7465             "  First image null");
7466
7467       (void) ThrowMagickException(exception,GetMagickModule(),
7468         CoderError,"image->next for first image is NULL but shouldn't be.",
7469         "`%s'",image_info->filename);
7470     }
7471
7472   if (mng_info->image_found == 0)
7473     {
7474       if (logging != MagickFalse)
7475         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7476           "  No visible images found.");
7477
7478       (void) ThrowMagickException(exception,GetMagickModule(),
7479         CoderError,"No visible images in file","`%s'",image_info->filename);
7480
7481       return(DestroyImageList(image));
7482     }
7483
7484   if (mng_info->ticks_per_second)
7485     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7486             final_delay/mng_info->ticks_per_second;
7487
7488   else
7489     image->start_loop=MagickTrue;
7490
7491   /* Find final nonzero image delay */
7492   final_image_delay=0;
7493
7494   while (GetNextImageInList(image) != (Image *) NULL)
7495     {
7496       if (image->delay)
7497         final_image_delay=image->delay;
7498
7499       image=GetNextImageInList(image);
7500     }
7501
7502   if (final_delay < final_image_delay)
7503     final_delay=final_image_delay;
7504
7505   image->delay=final_delay;
7506
7507   if (logging != MagickFalse)
7508       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7509         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7510         (double) final_delay);
7511
7512   if (logging != MagickFalse)
7513     {
7514       int
7515         scene;
7516
7517       scene=0;
7518       image=GetFirstImageInList(image);
7519
7520       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7521         "  Before coalesce:");
7522
7523       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7524         "    scene 0 delay=%.20g",(double) image->delay);
7525
7526       while (GetNextImageInList(image) != (Image *) NULL)
7527       {
7528         image=GetNextImageInList(image);
7529         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7530           "    scene %.20g delay=%.20g",(double) scene++,
7531           (double) image->delay);
7532       }
7533     }
7534
7535   image=GetFirstImageInList(image);
7536 #ifdef MNG_COALESCE_LAYERS
7537   if (insert_layers)
7538     {
7539       Image
7540         *next_image,
7541         *next;
7542
7543       size_t
7544         scene;
7545
7546       if (logging != MagickFalse)
7547         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7548           "  Coalesce Images");
7549
7550       scene=image->scene;
7551       next_image=CoalesceImages(image,exception);
7552
7553       if (next_image == (Image *) NULL)
7554         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7555
7556       image=DestroyImageList(image);
7557       image=next_image;
7558
7559       for (next=image; next != (Image *) NULL; next=next_image)
7560       {
7561          next->page.width=mng_info->mng_width;
7562          next->page.height=mng_info->mng_height;
7563          next->page.x=0;
7564          next->page.y=0;
7565          next->scene=scene++;
7566          next_image=GetNextImageInList(next);
7567
7568          if (next_image == (Image *) NULL)
7569            break;
7570
7571          if (next->delay == 0)
7572            {
7573              scene--;
7574              next_image->previous=GetPreviousImageInList(next);
7575              if (GetPreviousImageInList(next) == (Image *) NULL)
7576                image=next_image;
7577              else
7578                next->previous->next=next_image;
7579              next=DestroyImage(next);
7580            }
7581       }
7582     }
7583 #endif
7584
7585   while (GetNextImageInList(image) != (Image *) NULL)
7586       image=GetNextImageInList(image);
7587
7588   image->dispose=BackgroundDispose;
7589
7590   if (logging != MagickFalse)
7591     {
7592       int
7593         scene;
7594
7595       scene=0;
7596       image=GetFirstImageInList(image);
7597
7598       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7599         "  After coalesce:");
7600
7601       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7602         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7603         (double) image->dispose);
7604
7605       while (GetNextImageInList(image) != (Image *) NULL)
7606       {
7607         image=GetNextImageInList(image);
7608
7609         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7610           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7611           (double) image->delay,(double) image->dispose);
7612       }
7613    }
7614
7615   if (logging != MagickFalse)
7616     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7617       "  exit ReadOneMNGImage();");
7618
7619   return(image);
7620 }
7621
7622 static Image *ReadMNGImage(const ImageInfo *image_info,
7623      ExceptionInfo *exception)
7624 {
7625   Image
7626     *image;
7627
7628   MagickBooleanType
7629     logging,
7630     status;
7631
7632   MngInfo
7633     *mng_info;
7634
7635   /* Open image file.  */
7636
7637   assert(image_info != (const ImageInfo *) NULL);
7638   assert(image_info->signature == MagickCoreSignature);
7639   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7640      image_info->filename);
7641   assert(exception != (ExceptionInfo *) NULL);
7642   assert(exception->signature == MagickCoreSignature);
7643   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7644   image=AcquireImage(image_info,exception);
7645   mng_info=(MngInfo *) NULL;
7646   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7647
7648   if (status == MagickFalse)
7649     return((Image *) NULL);
7650
7651   /* Allocate a MngInfo structure.  */
7652
7653   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7654
7655   if (mng_info == (MngInfo *) NULL)
7656     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7657
7658   /* Initialize members of the MngInfo structure.  */
7659
7660   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7661   mng_info->image=image;
7662   image=ReadOneMNGImage(mng_info,image_info,exception);
7663   mng_info=MngInfoFreeStruct(mng_info);
7664
7665   if (image == (Image *) NULL)
7666     {
7667       if (logging != MagickFalse)
7668         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7669           "exit ReadMNGImage() with error");
7670
7671       return((Image *) NULL);
7672     }
7673   (void) CloseBlob(image);
7674
7675   if (logging != MagickFalse)
7676     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7677
7678   return(GetFirstImageInList(image));
7679 }
7680 #else /* PNG_LIBPNG_VER > 10011 */
7681 static Image *ReadPNGImage(const ImageInfo *image_info,
7682    ExceptionInfo *exception)
7683 {
7684   printf("Your PNG library is too old: You have libpng-%s\n",
7685      PNG_LIBPNG_VER_STRING);
7686
7687   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7688     "PNG library is too old","`%s'",image_info->filename);
7689
7690   return(Image *) NULL;
7691 }
7692
7693 static Image *ReadMNGImage(const ImageInfo *image_info,
7694    ExceptionInfo *exception)
7695 {
7696   return(ReadPNGImage(image_info,exception));
7697 }
7698 #endif /* PNG_LIBPNG_VER > 10011 */
7699 #endif
7700 \f
7701 /*
7702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7703 %                                                                             %
7704 %                                                                             %
7705 %                                                                             %
7706 %   R e g i s t e r P N G I m a g e                                           %
7707 %                                                                             %
7708 %                                                                             %
7709 %                                                                             %
7710 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7711 %
7712 %  RegisterPNGImage() adds properties for the PNG image format to
7713 %  the list of supported formats.  The properties include the image format
7714 %  tag, a method to read and/or write the format, whether the format
7715 %  supports the saving of more than one frame to the same file or blob,
7716 %  whether the format supports native in-memory I/O, and a brief
7717 %  description of the format.
7718 %
7719 %  The format of the RegisterPNGImage method is:
7720 %
7721 %      size_t RegisterPNGImage(void)
7722 %
7723 */
7724 ModuleExport size_t RegisterPNGImage(void)
7725 {
7726   char
7727     version[MagickPathExtent];
7728
7729   MagickInfo
7730     *entry;
7731
7732   static const char
7733     *PNGNote=
7734     {
7735       "See http://www.libpng.org/ for details about the PNG format."
7736     },
7737
7738     *JNGNote=
7739     {
7740       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7741       "format."
7742     },
7743
7744     *MNGNote=
7745     {
7746       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7747       "format."
7748     };
7749
7750   *version='\0';
7751
7752 #if defined(PNG_LIBPNG_VER_STRING)
7753   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7754   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7755    MagickPathExtent);
7756
7757   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7758     {
7759       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7760       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7761             MagickPathExtent);
7762     }
7763 #endif
7764
7765   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7766   entry->flags|=CoderDecoderSeekableStreamFlag;
7767
7768 #if defined(MAGICKCORE_PNG_DELEGATE)
7769   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7770   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7771 #endif
7772
7773   entry->magick=(IsImageFormatHandler *) IsMNG;
7774
7775   if (*version != '\0')
7776     entry->version=ConstantString(version);
7777
7778   entry->mime_type=ConstantString("video/x-mng");
7779   entry->note=ConstantString(MNGNote);
7780   (void) RegisterMagickInfo(entry);
7781
7782   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7783
7784 #if defined(MAGICKCORE_PNG_DELEGATE)
7785   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7786   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7787 #endif
7788
7789   entry->magick=(IsImageFormatHandler *) IsPNG;
7790   entry->flags|=CoderDecoderSeekableStreamFlag;
7791   entry->flags^=CoderAdjoinFlag;
7792   entry->mime_type=ConstantString("image/png");
7793
7794   if (*version != '\0')
7795     entry->version=ConstantString(version);
7796
7797   entry->note=ConstantString(PNGNote);
7798   (void) RegisterMagickInfo(entry);
7799
7800   entry=AcquireMagickInfo("PNG","PNG8",
7801     "8-bit indexed with optional binary transparency");
7802
7803 #if defined(MAGICKCORE_PNG_DELEGATE)
7804   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7805   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7806 #endif
7807
7808   entry->magick=(IsImageFormatHandler *) IsPNG;
7809   entry->flags|=CoderDecoderSeekableStreamFlag;
7810   entry->flags^=CoderAdjoinFlag;
7811   entry->mime_type=ConstantString("image/png");
7812   (void) RegisterMagickInfo(entry);
7813
7814   entry=AcquireMagickInfo("PNG","PNG24",
7815     "opaque or binary transparent 24-bit RGB");
7816   *version='\0';
7817
7818 #if defined(ZLIB_VERSION)
7819   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7820   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7821
7822   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7823     {
7824       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7825       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7826     }
7827 #endif
7828
7829   if (*version != '\0')
7830     entry->version=ConstantString(version);
7831
7832 #if defined(MAGICKCORE_PNG_DELEGATE)
7833   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7834   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7835 #endif
7836
7837   entry->magick=(IsImageFormatHandler *) IsPNG;
7838   entry->flags|=CoderDecoderSeekableStreamFlag;
7839   entry->flags^=CoderAdjoinFlag;
7840   entry->mime_type=ConstantString("image/png");
7841   (void) RegisterMagickInfo(entry);
7842
7843   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7844
7845 #if defined(MAGICKCORE_PNG_DELEGATE)
7846   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7847   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7848 #endif
7849
7850   entry->magick=(IsImageFormatHandler *) IsPNG;
7851   entry->flags|=CoderDecoderSeekableStreamFlag;
7852   entry->flags^=CoderAdjoinFlag;
7853   entry->mime_type=ConstantString("image/png");
7854   (void) RegisterMagickInfo(entry);
7855
7856   entry=AcquireMagickInfo("PNG","PNG48",
7857     "opaque or binary transparent 48-bit RGB");
7858
7859 #if defined(MAGICKCORE_PNG_DELEGATE)
7860   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7861   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7862 #endif
7863
7864   entry->magick=(IsImageFormatHandler *) IsPNG;
7865   entry->flags|=CoderDecoderSeekableStreamFlag;
7866   entry->flags^=CoderAdjoinFlag;
7867   entry->mime_type=ConstantString("image/png");
7868   (void) RegisterMagickInfo(entry);
7869
7870   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7871
7872 #if defined(MAGICKCORE_PNG_DELEGATE)
7873   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7874   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7875 #endif
7876
7877   entry->magick=(IsImageFormatHandler *) IsPNG;
7878   entry->flags|=CoderDecoderSeekableStreamFlag;
7879   entry->flags^=CoderAdjoinFlag;
7880   entry->mime_type=ConstantString("image/png");
7881   (void) RegisterMagickInfo(entry);
7882
7883   entry=AcquireMagickInfo("PNG","PNG00",
7884     "PNG inheriting bit-depth, color-type from original, if possible");
7885
7886 #if defined(MAGICKCORE_PNG_DELEGATE)
7887   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7888   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7889 #endif
7890
7891   entry->magick=(IsImageFormatHandler *) IsPNG;
7892   entry->flags|=CoderDecoderSeekableStreamFlag;
7893   entry->flags^=CoderAdjoinFlag;
7894   entry->mime_type=ConstantString("image/png");
7895   (void) RegisterMagickInfo(entry);
7896
7897   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7898
7899 #if defined(JNG_SUPPORTED)
7900 #if defined(MAGICKCORE_PNG_DELEGATE)
7901   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7902   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7903 #endif
7904 #endif
7905
7906   entry->magick=(IsImageFormatHandler *) IsJNG;
7907   entry->flags|=CoderDecoderSeekableStreamFlag;
7908   entry->flags^=CoderAdjoinFlag;
7909   entry->mime_type=ConstantString("image/x-jng");
7910   entry->note=ConstantString(JNGNote);
7911   (void) RegisterMagickInfo(entry);
7912
7913 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7914   ping_semaphore=AcquireSemaphoreInfo();
7915 #endif
7916
7917   return(MagickImageCoderSignature);
7918 }
7919 \f
7920 /*
7921 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7922 %                                                                             %
7923 %                                                                             %
7924 %                                                                             %
7925 %   U n r e g i s t e r P N G I m a g e                                       %
7926 %                                                                             %
7927 %                                                                             %
7928 %                                                                             %
7929 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7930 %
7931 %  UnregisterPNGImage() removes format registrations made by the
7932 %  PNG module from the list of supported formats.
7933 %
7934 %  The format of the UnregisterPNGImage method is:
7935 %
7936 %      UnregisterPNGImage(void)
7937 %
7938 */
7939 ModuleExport void UnregisterPNGImage(void)
7940 {
7941   (void) UnregisterMagickInfo("MNG");
7942   (void) UnregisterMagickInfo("PNG");
7943   (void) UnregisterMagickInfo("PNG8");
7944   (void) UnregisterMagickInfo("PNG24");
7945   (void) UnregisterMagickInfo("PNG32");
7946   (void) UnregisterMagickInfo("PNG48");
7947   (void) UnregisterMagickInfo("PNG64");
7948   (void) UnregisterMagickInfo("PNG00");
7949   (void) UnregisterMagickInfo("JNG");
7950
7951 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7952   if (ping_semaphore != (SemaphoreInfo *) NULL)
7953     RelinquishSemaphoreInfo(&ping_semaphore);
7954 #endif
7955 }
7956 \f
7957 #if defined(MAGICKCORE_PNG_DELEGATE)
7958 #if PNG_LIBPNG_VER > 10011
7959 /*
7960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7961 %                                                                             %
7962 %                                                                             %
7963 %                                                                             %
7964 %   W r i t e M N G I m a g e                                                 %
7965 %                                                                             %
7966 %                                                                             %
7967 %                                                                             %
7968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7969 %
7970 %  WriteMNGImage() writes an image in the Portable Network Graphics
7971 %  Group's "Multiple-image Network Graphics" encoded image format.
7972 %
7973 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7974 %
7975 %  The format of the WriteMNGImage method is:
7976 %
7977 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7978 %        Image *image,ExceptionInfo *exception)
7979 %
7980 %  A description of each parameter follows.
7981 %
7982 %    o image_info: the image info.
7983 %
7984 %    o image:  The image.
7985 %
7986 %    o exception: return any errors or warnings in this structure.
7987 %
7988 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7989 %    "To do" under ReadPNGImage):
7990 %
7991 %    Preserve all unknown and not-yet-handled known chunks found in input
7992 %    PNG file and copy them  into output PNG files according to the PNG
7993 %    copying rules.
7994 %
7995 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7996 %
7997 %    Improve selection of color type (use indexed-colour or indexed-colour
7998 %    with tRNS when 256 or fewer unique RGBA values are present).
7999 %
8000 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
8001 %    This will be complicated if we limit ourselves to generating MNG-LC
8002 %    files.  For now we ignore disposal method 3 and simply overlay the next
8003 %    image on it.
8004 %
8005 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
8006 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
8007 %    [mostly done 15 June 1999 but still need to take care of tRNS]
8008 %
8009 %    Check for identical sRGB and replace with a global sRGB (and remove
8010 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
8011 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
8012 %    local gAMA/cHRM with local sRGB if appropriate).
8013 %
8014 %    Check for identical sBIT chunks and write global ones.
8015 %
8016 %    Provide option to skip writing the signature tEXt chunks.
8017 %
8018 %    Use signatures to detect identical objects and reuse the first
8019 %    instance of such objects instead of writing duplicate objects.
8020 %
8021 %    Use a smaller-than-32k value of compression window size when
8022 %    appropriate.
8023 %
8024 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
8025 %    ancillary text chunks and save profiles.
8026 %
8027 %    Provide an option to force LC files (to ensure exact framing rate)
8028 %    instead of VLC.
8029 %
8030 %    Provide an option to force VLC files instead of LC, even when offsets
8031 %    are present.  This will involve expanding the embedded images with a
8032 %    transparent region at the top and/or left.
8033 */
8034
8035 static void
8036 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
8037    png_info *ping_info, unsigned char *profile_type, unsigned char
8038    *profile_description, unsigned char *profile_data, png_uint_32 length)
8039 {
8040    png_textp
8041      text;
8042
8043    register ssize_t
8044      i;
8045
8046    unsigned char
8047      *sp;
8048
8049    png_charp
8050      dp;
8051
8052    png_uint_32
8053      allocated_length,
8054      description_length;
8055
8056    unsigned char
8057      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
8058
8059    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
8060       return;
8061
8062    if (image_info->verbose)
8063      {
8064        (void) printf("writing raw profile: type=%s, length=%.20g\n",
8065          (char *) profile_type, (double) length);
8066      }
8067
8068 #if PNG_LIBPNG_VER >= 10400
8069    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
8070 #else
8071    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
8072 #endif
8073    description_length=(png_uint_32) strlen((const char *) profile_description);
8074    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
8075       + description_length);
8076 #if PNG_LIBPNG_VER >= 10400
8077    text[0].text=(png_charp) png_malloc(ping,
8078       (png_alloc_size_t) allocated_length);
8079    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
8080 #else
8081    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
8082    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
8083 #endif
8084    text[0].key[0]='\0';
8085    (void) ConcatenateMagickString(text[0].key,
8086       "Raw profile type ",MagickPathExtent);
8087    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
8088    sp=profile_data;
8089    dp=text[0].text;
8090    *dp++='\n';
8091    (void) CopyMagickString(dp,(const char *) profile_description,
8092      allocated_length);
8093    dp+=description_length;
8094    *dp++='\n';
8095    (void) FormatLocaleString(dp,allocated_length-
8096      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
8097    dp+=8;
8098
8099    for (i=0; i < (ssize_t) length; i++)
8100    {
8101      if (i%36 == 0)
8102        *dp++='\n';
8103      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
8104      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
8105    }
8106
8107    *dp++='\n';
8108    *dp='\0';
8109    text[0].text_length=(png_size_t) (dp-text[0].text);
8110    text[0].compression=image_info->compression == NoCompression ||
8111      (image_info->compression == UndefinedCompression &&
8112      text[0].text_length < 128) ? -1 : 0;
8113
8114    if (text[0].text_length <= allocated_length)
8115      png_set_text(ping,ping_info,text,1);
8116
8117    png_free(ping,text[0].text);
8118    png_free(ping,text[0].key);
8119    png_free(ping,text);
8120 }
8121
8122 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8123   const Quantum *p, const PixelInfo *q)
8124 {
8125   MagickRealType
8126     value;
8127
8128   value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8129   if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8130     return(MagickFalse);
8131   value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8132   if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8133     return(MagickFalse);
8134   value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8135   if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8136     return(MagickFalse);
8137
8138   return(MagickTrue);
8139 }
8140
8141 #if defined(PNG_tIME_SUPPORTED)
8142 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8143   const char *date,ExceptionInfo *exception)
8144 {
8145   const char
8146     *timestamp;
8147
8148   int
8149     ret;
8150
8151   unsigned int
8152     day,
8153     hour,
8154     minute,
8155     month,
8156     second,
8157     year;
8158
8159   int
8160     addhours=0,
8161     addminutes=0;
8162
8163   png_time
8164     ptime;
8165
8166   if (date == (const char *) NULL)
8167     timestamp=GetImageProperty(image,"date:modify",exception);
8168   else
8169     timestamp=date;
8170
8171   LogMagickEvent(CoderEvent,GetMagickModule(),
8172       "  Writing tIME chunk: timestamp property is %30s\n",timestamp);
8173   ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d",&year,&month,&day,&hour,
8174       &minute, &second);
8175   addhours=0;     
8176   addminutes=0;     
8177   ret=sscanf(timestamp,"%d-%d-%dT%d:%d:%d%d:%d",&year,&month,&day,&hour,
8178       &minute, &second, &addhours, &addminutes);
8179     LogMagickEvent(CoderEvent,GetMagickModule(),
8180       "   Date format specified for png:tIME=%s" ,timestamp);
8181     LogMagickEvent(CoderEvent,GetMagickModule(),
8182       "      ret=%d,y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, as=%d",
8183       ret,year,month,day,hour,minute,second,addhours,addminutes);
8184   if (ret < 6)
8185   {
8186     LogMagickEvent(CoderEvent,GetMagickModule(),
8187       "      Invalid date, ret=%d",ret);
8188     (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8189       "Invalid date format specified for png:tIME","`%s' (ret=%d)",
8190       image->filename,ret);
8191     return;
8192   }
8193   ptime.year=(png_uint_16) year;
8194   ptime.month=(png_byte) month;
8195   ptime.day=(png_byte) day;
8196   if (addhours < 0)
8197   {
8198     addhours+=24;
8199     ptime.hour=(png_byte) hour+addhours;
8200     ptime.day--;
8201     if (ptime.day == 0)
8202     {
8203       /* wrong for short months */
8204       ptime.month--;
8205       ptime.day=31;
8206     }
8207     if (ptime.month == 0)
8208     {
8209       ptime.month++;
8210       ptime.year--;
8211     }
8212   }
8213   ptime.hour=(png_byte) hour+addhours;
8214   ptime.minute=(png_byte) minute+addminutes;
8215   ptime.second=(png_byte) second;
8216   if (ptime.minute > 60)
8217   {
8218      ptime.hour++;
8219      ptime.minute-=60;
8220   }
8221   if (ptime.hour > 24)
8222   {
8223      ptime.day ++;
8224      ptime.hour -=24;
8225   }
8226   if (ptime.hour < 0)
8227   {
8228      ptime.day --;
8229      ptime.hour +=24;
8230   }
8231   /* To do: fix this for leap years */
8232   if (ptime.day > 31 || (ptime.month == 2 && ptime.day > 28) ||
8233       ((ptime.month == 4 || ptime.month == 6 || ptime.month == 9 ||
8234       ptime.month == 11) && ptime.day > 30))
8235   {
8236      ptime.month++;
8237      ptime.day = 1;
8238   }
8239   if (ptime.month > 12)
8240   {
8241      ptime.year++;
8242      ptime.month=1;
8243   }
8244
8245   LogMagickEvent(CoderEvent,GetMagickModule(),
8246       "      png_set_tIME: y=%d, m=%d, d=%d, h=%d, m=%d, s=%d, ah=%d, am=%d",
8247       ptime.year, ptime.month, ptime.day, ptime.hour, ptime.minute,
8248       ptime.second, addhours, addminutes);
8249   png_set_tIME(ping,info,&ptime);
8250 }
8251 #endif
8252
8253 /* Write one PNG image */
8254 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8255   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8256 {
8257   char
8258     im_vers[32],
8259     libpng_runv[32],
8260     libpng_vers[32],
8261     zlib_runv[32],
8262     zlib_vers[32];
8263
8264   Image
8265     *image;
8266
8267   ImageInfo
8268     *image_info;
8269
8270   char
8271     s[2];
8272
8273   const char
8274     *name,
8275     *property,
8276     *value;
8277
8278   const StringInfo
8279     *profile;
8280
8281   int
8282     num_passes,
8283     pass,
8284     ping_wrote_caNv;
8285
8286   png_byte
8287      ping_trans_alpha[256];
8288
8289   png_color
8290      palette[257];
8291
8292   png_color_16
8293     ping_background,
8294     ping_trans_color;
8295
8296   png_info
8297     *ping_info;
8298
8299   png_struct
8300     *ping;
8301
8302   png_uint_32
8303     ping_height,
8304     ping_width;
8305
8306   ssize_t
8307     y;
8308
8309   MagickBooleanType
8310     image_matte,
8311     logging,
8312     matte,
8313
8314     ping_have_blob,
8315     ping_have_cheap_transparency,
8316     ping_have_color,
8317     ping_have_non_bw,
8318     ping_have_PLTE,
8319     ping_have_bKGD,
8320     ping_have_eXIf,
8321     ping_have_iCCP,
8322     ping_have_pHYs,
8323     ping_have_sRGB,
8324     ping_have_tRNS,
8325
8326     ping_exclude_bKGD,
8327     ping_exclude_cHRM,
8328     ping_exclude_date,
8329     /* ping_exclude_EXIF, */
8330     ping_exclude_eXIf,
8331     ping_exclude_gAMA,
8332     ping_exclude_iCCP,
8333     /* ping_exclude_iTXt, */
8334     ping_exclude_oFFs,
8335     ping_exclude_pHYs,
8336     ping_exclude_sRGB,
8337     ping_exclude_tEXt,
8338     ping_exclude_tIME,
8339     /* ping_exclude_tRNS, */
8340     ping_exclude_caNv,
8341     ping_exclude_zCCP, /* hex-encoded iCCP */
8342     ping_exclude_zTXt,
8343
8344     ping_preserve_colormap,
8345     ping_preserve_iCCP,
8346     ping_need_colortype_warning,
8347
8348     status,
8349     tried_332,
8350     tried_333,
8351     tried_444;
8352
8353   MemoryInfo
8354     *volatile pixel_info;
8355
8356   QuantumInfo
8357     *quantum_info;
8358
8359   PNGErrorInfo
8360     error_info;
8361
8362   register ssize_t
8363     i,
8364     x;
8365
8366   unsigned char
8367     *ping_pixels;
8368
8369   volatile int
8370     image_colors,
8371     ping_bit_depth,
8372     ping_color_type,
8373     ping_interlace_method,
8374     ping_compression_method,
8375     ping_filter_method,
8376     ping_num_trans;
8377
8378   volatile size_t
8379     image_depth,
8380     old_bit_depth;
8381
8382   size_t
8383     quality,
8384     rowbytes,
8385     save_image_depth;
8386
8387   int
8388     j,
8389     number_colors,
8390     number_opaque,
8391     number_semitransparent,
8392     number_transparent,
8393     ping_pHYs_unit_type;
8394
8395   png_uint_32
8396     ping_pHYs_x_resolution,
8397     ping_pHYs_y_resolution;
8398
8399   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8400     "  Enter WriteOnePNGImage()");
8401
8402   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8403   if (image == (Image *) NULL)
8404     return(MagickFalse);
8405   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8406   if (image_info == (ImageInfo *) NULL)
8407     ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8408
8409   /* Define these outside of the following "if logging()" block so they will
8410    * show in debuggers.
8411    */
8412   *im_vers='\0';
8413   (void) ConcatenateMagickString(im_vers,
8414          MagickLibVersionText,MagickPathExtent);
8415   (void) ConcatenateMagickString(im_vers,
8416          MagickLibAddendum,MagickPathExtent);
8417
8418   *libpng_vers='\0';
8419   (void) ConcatenateMagickString(libpng_vers,
8420          PNG_LIBPNG_VER_STRING,32);
8421   *libpng_runv='\0';
8422   (void) ConcatenateMagickString(libpng_runv,
8423          png_get_libpng_ver(NULL),32);
8424
8425   *zlib_vers='\0';
8426   (void) ConcatenateMagickString(zlib_vers,
8427          ZLIB_VERSION,32);
8428   *zlib_runv='\0';
8429   (void) ConcatenateMagickString(zlib_runv,
8430          zlib_version,32);
8431
8432   if (logging != MagickFalse)
8433     {
8434        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8435           "    IM version     = %s", im_vers);
8436        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8437           "    Libpng version = %s", libpng_vers);
8438        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8439        {
8440        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8441           "      running with   %s", libpng_runv);
8442        }
8443        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8444           "    Zlib version   = %s", zlib_vers);
8445        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8446        {
8447        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8448           "      running with   %s", zlib_runv);
8449        }
8450     }
8451
8452   /* Initialize some stuff */
8453   ping_bit_depth=0,
8454   ping_color_type=0,
8455   ping_interlace_method=0,
8456   ping_compression_method=0,
8457   ping_filter_method=0,
8458   ping_num_trans = 0;
8459
8460   ping_background.red = 0;
8461   ping_background.green = 0;
8462   ping_background.blue = 0;
8463   ping_background.gray = 0;
8464   ping_background.index = 0;
8465
8466   ping_trans_color.red=0;
8467   ping_trans_color.green=0;
8468   ping_trans_color.blue=0;
8469   ping_trans_color.gray=0;
8470
8471   ping_pHYs_unit_type = 0;
8472   ping_pHYs_x_resolution = 0;
8473   ping_pHYs_y_resolution = 0;
8474
8475   ping_have_blob=MagickFalse;
8476   ping_have_cheap_transparency=MagickFalse;
8477   ping_have_color=MagickTrue;
8478   ping_have_non_bw=MagickTrue;
8479   ping_have_PLTE=MagickFalse;
8480   ping_have_bKGD=MagickFalse;
8481   ping_have_eXIf=MagickTrue;
8482   ping_have_iCCP=MagickFalse;
8483   ping_have_pHYs=MagickFalse;
8484   ping_have_sRGB=MagickFalse;
8485   ping_have_tRNS=MagickFalse;
8486
8487   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8488   ping_exclude_caNv=mng_info->ping_exclude_caNv;
8489   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8490   ping_exclude_date=mng_info->ping_exclude_date;
8491   ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8492   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8493   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8494   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8495   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8496   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8497   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8498   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8499   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8500   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8501   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8502   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8503
8504   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8505   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8506   ping_need_colortype_warning = MagickFalse;
8507
8508   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8509    * i.e., eliminate the ICC profile and set image->rendering_intent.
8510    * Note that this will not involve any changes to the actual pixels
8511    * but merely passes information to applications that read the resulting
8512    * PNG image.
8513    *
8514    * To do: recognize other variants of the sRGB profile, using the CRC to
8515    * verify all recognized variants including the 7 already known.
8516    *
8517    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8518    *
8519    * Use something other than image->rendering_intent to record the fact
8520    * that the sRGB profile was found.
8521    *
8522    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8523    * profile.  Record the Blackpoint Compensation, if any.
8524    */
8525    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8526    {
8527       char
8528         *name;
8529
8530       const StringInfo
8531         *profile;
8532
8533       ResetImageProfileIterator(image);
8534       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8535       {
8536         profile=GetImageProfile(image,name);
8537
8538         if (profile != (StringInfo *) NULL)
8539           {
8540             if ((LocaleCompare(name,"ICC") == 0) ||
8541                 (LocaleCompare(name,"ICM") == 0))
8542
8543              {
8544                  int
8545                    icheck,
8546                    got_crc=0;
8547
8548
8549                  png_uint_32
8550                    length,
8551                    profile_crc=0;
8552
8553                  unsigned char
8554                    *data;
8555
8556                  length=(png_uint_32) GetStringInfoLength(profile);
8557
8558                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8559                  {
8560                    if (length == sRGB_info[icheck].len)
8561                    {
8562                      if (got_crc == 0)
8563                      {
8564                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8565                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8566                          (unsigned long) length);
8567
8568                        data=GetStringInfoDatum(profile);
8569                        profile_crc=crc32(0,data,length);
8570
8571                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8572                            "      with crc=%8x",(unsigned int) profile_crc);
8573                        got_crc++;
8574                      }
8575
8576                      if (profile_crc == sRGB_info[icheck].crc)
8577                      {
8578                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8579                             "      It is sRGB with rendering intent = %s",
8580                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8581                              sRGB_info[icheck].intent));
8582                         if (image->rendering_intent==UndefinedIntent)
8583                         {
8584                           image->rendering_intent=
8585                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8586                              sRGB_info[icheck].intent);
8587                         }
8588                         ping_exclude_iCCP = MagickTrue;
8589                         ping_exclude_zCCP = MagickTrue;
8590                         ping_have_sRGB = MagickTrue;
8591                         break;
8592                      }
8593                    }
8594                  }
8595                  if (sRGB_info[icheck].len == 0)
8596                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8597                         "    Got %lu-byte ICC profile not recognized as sRGB",
8598                         (unsigned long) length);
8599               }
8600           }
8601         name=GetNextImageProfile(image);
8602       }
8603   }
8604
8605   number_opaque = 0;
8606   number_semitransparent = 0;
8607   number_transparent = 0;
8608
8609   if (logging != MagickFalse)
8610     {
8611       if (image->storage_class == UndefinedClass)
8612           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8613           "    image->storage_class=UndefinedClass");
8614       if (image->storage_class == DirectClass)
8615           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8616           "    image->storage_class=DirectClass");
8617       if (image->storage_class == PseudoClass)
8618           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8619           "    image->storage_class=PseudoClass");
8620       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8621           "    image->taint=MagickTrue":
8622           "    image->taint=MagickFalse");
8623       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8624           "    image->gamma=%g", image->gamma);
8625     }
8626
8627   if (image->storage_class == PseudoClass &&
8628      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8629      mng_info->write_png48 || mng_info->write_png64 ||
8630      (mng_info->write_png_colortype != 1 &&
8631      mng_info->write_png_colortype != 5)))
8632     {
8633       (void) SyncImage(image,exception);
8634       image->storage_class = DirectClass;
8635     }
8636
8637   if (ping_preserve_colormap == MagickFalse)
8638     {
8639       if (image->storage_class != PseudoClass && image->colormap != NULL)
8640         {
8641           /* Free the bogus colormap; it can cause trouble later */
8642            if (logging != MagickFalse)
8643               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8644               "    Freeing bogus colormap");
8645            (void) RelinquishMagickMemory(image->colormap);
8646            image->colormap=NULL;
8647         }
8648     }
8649
8650   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8651     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8652
8653   /*
8654     Sometimes we get PseudoClass images whose RGB values don't match
8655     the colors in the colormap.  This code syncs the RGB values.
8656   */
8657   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8658      (void) SyncImage(image,exception);
8659
8660 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8661   if (image->depth > 8)
8662     {
8663       if (logging != MagickFalse)
8664         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8665           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8666
8667       image->depth=8;
8668     }
8669 #endif
8670
8671   /* Respect the -depth option */
8672   if (image->depth < 4)
8673     {
8674        register Quantum
8675          *r;
8676
8677        if (image->depth > 2)
8678          {
8679            /* Scale to 4-bit */
8680            LBR04PacketRGBO(image->background_color);
8681
8682            for (y=0; y < (ssize_t) image->rows; y++)
8683            {
8684              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8685
8686              if (r == (Quantum *) NULL)
8687                break;
8688
8689              for (x=0; x < (ssize_t) image->columns; x++)
8690              {
8691                 LBR04PixelRGBA(r);
8692                 r+=GetPixelChannels(image);
8693              }
8694
8695              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8696                 break;
8697            }
8698
8699            if (image->storage_class == PseudoClass && image->colormap != NULL)
8700            {
8701              for (i=0; i < (ssize_t) image->colors; i++)
8702              {
8703                LBR04PacketRGBO(image->colormap[i]);
8704              }
8705            }
8706          }
8707        else if (image->depth > 1)
8708          {
8709            /* Scale to 2-bit */
8710            LBR02PacketRGBO(image->background_color);
8711
8712            for (y=0; y < (ssize_t) image->rows; y++)
8713            {
8714              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8715
8716              if (r == (Quantum *) NULL)
8717                break;
8718
8719              for (x=0; x < (ssize_t) image->columns; x++)
8720              {
8721                 LBR02PixelRGBA(r);
8722                 r+=GetPixelChannels(image);
8723              }
8724
8725              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8726                 break;
8727            }
8728
8729            if (image->storage_class == PseudoClass && image->colormap != NULL)
8730            {
8731              for (i=0; i < (ssize_t) image->colors; i++)
8732              {
8733                LBR02PacketRGBO(image->colormap[i]);
8734              }
8735            }
8736          }
8737        else
8738          {
8739            /* Scale to 1-bit */
8740            LBR01PacketRGBO(image->background_color);
8741
8742            for (y=0; y < (ssize_t) image->rows; y++)
8743            {
8744              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8745
8746              if (r == (Quantum *) NULL)
8747                break;
8748
8749              for (x=0; x < (ssize_t) image->columns; x++)
8750              {
8751                 LBR01PixelRGBA(r);
8752                 r+=GetPixelChannels(image);
8753              }
8754
8755              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8756                 break;
8757            }
8758
8759            if (image->storage_class == PseudoClass && image->colormap != NULL)
8760            {
8761              for (i=0; i < (ssize_t) image->colors; i++)
8762              {
8763                LBR01PacketRGBO(image->colormap[i]);
8764              }
8765            }
8766          }
8767     }
8768
8769   /* To do: set to next higher multiple of 8 */
8770   if (image->depth < 8)
8771      image->depth=8;
8772
8773 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8774   /* PNG does not handle depths greater than 16 so reduce it even
8775    * if lossy
8776    */
8777   if (image->depth > 8)
8778       image->depth=16;
8779 #endif
8780
8781 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8782   if (image->depth > 8)
8783     {
8784       /* To do: fill low byte properly */
8785       image->depth=16;
8786     }
8787
8788   if (image->depth == 16 && mng_info->write_png_depth != 16)
8789     if (mng_info->write_png8 ||
8790         LosslessReduceDepthOK(image,exception) != MagickFalse)
8791       image->depth = 8;
8792 #endif
8793
8794   image_colors = (int) image->colors;
8795   number_opaque = (int) image->colors;
8796   number_transparent = 0;
8797   number_semitransparent = 0;
8798
8799   if (mng_info->write_png_colortype &&
8800      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8801      mng_info->write_png_colortype < 4 &&
8802      image->alpha_trait == UndefinedPixelTrait)))
8803   {
8804      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8805       * are not going to need the result.
8806       */
8807      if (mng_info->write_png_colortype == 1 ||
8808         mng_info->write_png_colortype == 5)
8809        ping_have_color=MagickFalse;
8810
8811      if (image->alpha_trait != UndefinedPixelTrait)
8812        {
8813          number_transparent = 2;
8814          number_semitransparent = 1;
8815        }
8816   }
8817
8818   if (mng_info->write_png_colortype < 7)
8819   {
8820   /* BUILD_PALETTE
8821    *
8822    * Normally we run this just once, but in the case of writing PNG8
8823    * we reduce the transparency to binary and run again, then if there
8824    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8825    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8826    * palette.  Then (To do) we take care of a final reduction that is only
8827    * needed if there are still 256 colors present and one of them has both
8828    * transparent and opaque instances.
8829    */
8830
8831   tried_332 = MagickFalse;
8832   tried_333 = MagickFalse;
8833   tried_444 = MagickFalse;
8834
8835   for (j=0; j<6; j++)
8836   {
8837     /*
8838      * Sometimes we get DirectClass images that have 256 colors or fewer.
8839      * This code will build a colormap.
8840      *
8841      * Also, sometimes we get PseudoClass images with an out-of-date
8842      * colormap.  This code will replace the colormap with a new one.
8843      * Sometimes we get PseudoClass images that have more than 256 colors.
8844      * This code will delete the colormap and change the image to
8845      * DirectClass.
8846      *
8847      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8848      * even though it sometimes contains left-over non-opaque values.
8849      *
8850      * Also we gather some information (number of opaque, transparent,
8851      * and semitransparent pixels, and whether the image has any non-gray
8852      * pixels or only black-and-white pixels) that we might need later.
8853      *
8854      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8855      * we need to check for bogus non-opaque values, at least.
8856      */
8857
8858    int
8859      n;
8860
8861    PixelInfo
8862      opaque[260],
8863      semitransparent[260],
8864      transparent[260];
8865
8866    register const Quantum
8867      *s;
8868
8869    register Quantum
8870      *q,
8871      *r;
8872
8873    if (logging != MagickFalse)
8874      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8875          "    Enter BUILD_PALETTE:");
8876
8877    if (logging != MagickFalse)
8878      {
8879        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8880              "      image->columns=%.20g",(double) image->columns);
8881        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8882              "      image->rows=%.20g",(double) image->rows);
8883        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8884              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8885        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8886              "      image->depth=%.20g",(double) image->depth);
8887
8888        if (image->storage_class == PseudoClass && image->colormap != NULL)
8889        {
8890          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8891              "      Original colormap:");
8892          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8893              "        i    (red,green,blue,alpha)");
8894
8895          for (i=0; i < 256; i++)
8896          {
8897                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8898                    "        %d    (%d,%d,%d,%d)",
8899                     (int) i,
8900                     (int) image->colormap[i].red,
8901                     (int) image->colormap[i].green,
8902                     (int) image->colormap[i].blue,
8903                     (int) image->colormap[i].alpha);
8904          }
8905
8906          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8907          {
8908            if (i > 255)
8909              {
8910                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8911                    "        %d    (%d,%d,%d,%d)",
8912                     (int) i,
8913                     (int) image->colormap[i].red,
8914                     (int) image->colormap[i].green,
8915                     (int) image->colormap[i].blue,
8916                     (int) image->colormap[i].alpha);
8917              }
8918          }
8919        }
8920
8921        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8922            "      image->colors=%d",(int) image->colors);
8923
8924        if (image->colors == 0)
8925          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8926              "        (zero means unknown)");
8927
8928        if (ping_preserve_colormap == MagickFalse)
8929          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8930               "      Regenerate the colormap");
8931      }
8932
8933      image_colors=0;
8934      number_opaque = 0;
8935      number_semitransparent = 0;
8936      number_transparent = 0;
8937
8938      for (y=0; y < (ssize_t) image->rows; y++)
8939      {
8940        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8941
8942        if (q == (Quantum *) NULL)
8943          break;
8944
8945        for (x=0; x < (ssize_t) image->columns; x++)
8946        {
8947            if (image->alpha_trait == UndefinedPixelTrait ||
8948               GetPixelAlpha(image,q) == OpaqueAlpha)
8949              {
8950                if (number_opaque < 259)
8951                  {
8952                    if (number_opaque == 0)
8953                      {
8954                        GetPixelInfoPixel(image, q, opaque);
8955                        opaque[0].alpha=OpaqueAlpha;
8956                        number_opaque=1;
8957                      }
8958
8959                    for (i=0; i< (ssize_t) number_opaque; i++)
8960                      {
8961                        if (Magick_png_color_equal(image,q,opaque+i))
8962                          break;
8963                      }
8964
8965                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8966                      {
8967                        number_opaque++;
8968                        GetPixelInfoPixel(image, q, opaque+i);
8969                        opaque[i].alpha=OpaqueAlpha;
8970                      }
8971                  }
8972              }
8973            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8974              {
8975                if (number_transparent < 259)
8976                  {
8977                    if (number_transparent == 0)
8978                      {
8979                        GetPixelInfoPixel(image, q, transparent);
8980                        ping_trans_color.red=(unsigned short)
8981                          GetPixelRed(image,q);
8982                        ping_trans_color.green=(unsigned short)
8983                          GetPixelGreen(image,q);
8984                        ping_trans_color.blue=(unsigned short)
8985                          GetPixelBlue(image,q);
8986                        ping_trans_color.gray=(unsigned short)
8987                          GetPixelGray(image,q);
8988                        number_transparent = 1;
8989                      }
8990
8991                    for (i=0; i< (ssize_t) number_transparent; i++)
8992                      {
8993                        if (Magick_png_color_equal(image,q,transparent+i))
8994                          break;
8995                      }
8996
8997                    if (i ==  (ssize_t) number_transparent &&
8998                        number_transparent < 259)
8999                      {
9000                        number_transparent++;
9001                        GetPixelInfoPixel(image,q,transparent+i);
9002                      }
9003                  }
9004              }
9005            else
9006              {
9007                if (number_semitransparent < 259)
9008                  {
9009                    if (number_semitransparent == 0)
9010                      {
9011                        GetPixelInfoPixel(image,q,semitransparent);
9012                        number_semitransparent = 1;
9013                      }
9014
9015                    for (i=0; i< (ssize_t) number_semitransparent; i++)
9016                      {
9017                        if (Magick_png_color_equal(image,q,semitransparent+i)
9018                            && GetPixelAlpha(image,q) ==
9019                            semitransparent[i].alpha)
9020                          break;
9021                      }
9022
9023                    if (i ==  (ssize_t) number_semitransparent &&
9024                        number_semitransparent < 259)
9025                      {
9026                        number_semitransparent++;
9027                        GetPixelInfoPixel(image, q, semitransparent+i);
9028                      }
9029                  }
9030              }
9031            q+=GetPixelChannels(image);
9032         }
9033      }
9034
9035      if (mng_info->write_png8 == MagickFalse &&
9036          ping_exclude_bKGD == MagickFalse)
9037        {
9038          /* Add the background color to the palette, if it
9039           * isn't already there.
9040           */
9041           if (logging != MagickFalse)
9042             {
9043               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9044                   "      Check colormap for background (%d,%d,%d)",
9045                   (int) image->background_color.red,
9046                   (int) image->background_color.green,
9047                   (int) image->background_color.blue);
9048             }
9049           for (i=0; i<number_opaque; i++)
9050           {
9051              if (opaque[i].red == image->background_color.red &&
9052                  opaque[i].green == image->background_color.green &&
9053                  opaque[i].blue == image->background_color.blue)
9054                break;
9055           }
9056           if (number_opaque < 259 && i == number_opaque)
9057             {
9058                opaque[i] = image->background_color;
9059                ping_background.index = i;
9060                number_opaque++;
9061                if (logging != MagickFalse)
9062                  {
9063                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9064                        "      background_color index is %d",(int) i);
9065                  }
9066
9067             }
9068           else if (logging != MagickFalse)
9069               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9070                   "      No room in the colormap to add background color");
9071        }
9072
9073      image_colors=number_opaque+number_transparent+number_semitransparent;
9074
9075      if (logging != MagickFalse)
9076        {
9077          if (image_colors > 256)
9078             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9079                   "      image has more than 256 colors");
9080
9081          else
9082             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9083                   "      image has %d colors",image_colors);
9084        }
9085
9086      if (ping_preserve_colormap != MagickFalse)
9087        break;
9088
9089      if (mng_info->write_png_colortype != 7) /* We won't need this info */
9090        {
9091          ping_have_color=MagickFalse;
9092          ping_have_non_bw=MagickFalse;
9093
9094          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
9095          {
9096            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9097               "incompatible colorspace");
9098            ping_have_color=MagickTrue;
9099            ping_have_non_bw=MagickTrue;
9100          }
9101
9102          if(image_colors > 256)
9103            {
9104              for (y=0; y < (ssize_t) image->rows; y++)
9105              {
9106                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9107
9108                if (q == (Quantum *) NULL)
9109                  break;
9110
9111                s=q;
9112                for (x=0; x < (ssize_t) image->columns; x++)
9113                {
9114                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
9115                      GetPixelRed(image,s) != GetPixelBlue(image,s))
9116                    {
9117                       ping_have_color=MagickTrue;
9118                       ping_have_non_bw=MagickTrue;
9119                       break;
9120                    }
9121                  s+=GetPixelChannels(image);
9122                }
9123
9124                if (ping_have_color != MagickFalse)
9125                  break;
9126
9127                /* Worst case is black-and-white; we are looking at every
9128                 * pixel twice.
9129                 */
9130
9131                if (ping_have_non_bw == MagickFalse)
9132                  {
9133                    s=q;
9134                    for (x=0; x < (ssize_t) image->columns; x++)
9135                    {
9136                      if (GetPixelRed(image,s) != 0 &&
9137                          GetPixelRed(image,s) != QuantumRange)
9138                        {
9139                          ping_have_non_bw=MagickTrue;
9140                          break;
9141                        }
9142                      s+=GetPixelChannels(image);
9143                    }
9144                }
9145              }
9146            }
9147        }
9148
9149      if (image_colors < 257)
9150        {
9151          PixelInfo
9152            colormap[260];
9153
9154          /*
9155           * Initialize image colormap.
9156           */
9157
9158          if (logging != MagickFalse)
9159             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9160                   "      Sort the new colormap");
9161
9162         /* Sort palette, transparent first */;
9163
9164          n = 0;
9165
9166          for (i=0; i<number_transparent; i++)
9167             colormap[n++] = transparent[i];
9168
9169          for (i=0; i<number_semitransparent; i++)
9170             colormap[n++] = semitransparent[i];
9171
9172          for (i=0; i<number_opaque; i++)
9173             colormap[n++] = opaque[i];
9174
9175          ping_background.index +=
9176            (number_transparent + number_semitransparent);
9177
9178          /* image_colors < 257; search the colormap instead of the pixels
9179           * to get ping_have_color and ping_have_non_bw
9180           */
9181          for (i=0; i<n; i++)
9182          {
9183            if (ping_have_color == MagickFalse)
9184              {
9185                 if (colormap[i].red != colormap[i].green ||
9186                     colormap[i].red != colormap[i].blue)
9187                   {
9188                      ping_have_color=MagickTrue;
9189                      ping_have_non_bw=MagickTrue;
9190                      break;
9191                   }
9192               }
9193
9194            if (ping_have_non_bw == MagickFalse)
9195              {
9196                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9197                    ping_have_non_bw=MagickTrue;
9198              }
9199           }
9200
9201         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9202             (number_transparent == 0 && number_semitransparent == 0)) &&
9203             (((mng_info->write_png_colortype-1) ==
9204             PNG_COLOR_TYPE_PALETTE) ||
9205             (mng_info->write_png_colortype == 0)))
9206           {
9207             if (logging != MagickFalse)
9208               {
9209                 if (n !=  (ssize_t) image_colors)
9210                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9211                    "   image_colors (%d) and n (%d)  don't match",
9212                    image_colors, n);
9213
9214                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9215                    "      AcquireImageColormap");
9216               }
9217
9218             image->colors = image_colors;
9219
9220             if (AcquireImageColormap(image,image_colors,exception) ==
9221                 MagickFalse)
9222                ThrowWriterException(ResourceLimitError,
9223                    "MemoryAllocationFailed");
9224
9225             for (i=0; i< (ssize_t) image_colors; i++)
9226                image->colormap[i] = colormap[i];
9227
9228             if (logging != MagickFalse)
9229               {
9230                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9231                       "      image->colors=%d (%d)",
9232                       (int) image->colors, image_colors);
9233
9234                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9235                       "      Update the pixel indexes");
9236               }
9237
9238             /* Sync the pixel indices with the new colormap */
9239
9240             for (y=0; y < (ssize_t) image->rows; y++)
9241             {
9242               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9243
9244               if (q == (Quantum *) NULL)
9245                 break;
9246
9247               for (x=0; x < (ssize_t) image->columns; x++)
9248               {
9249                 for (i=0; i< (ssize_t) image_colors; i++)
9250                 {
9251                   if ((image->alpha_trait == UndefinedPixelTrait ||
9252                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9253                       image->colormap[i].red == GetPixelRed(image,q) &&
9254                       image->colormap[i].green == GetPixelGreen(image,q) &&
9255                       image->colormap[i].blue == GetPixelBlue(image,q))
9256                   {
9257                     SetPixelIndex(image,i,q);
9258                     break;
9259                   }
9260                 }
9261                 q+=GetPixelChannels(image);
9262               }
9263
9264               if (SyncAuthenticPixels(image,exception) == MagickFalse)
9265                  break;
9266             }
9267           }
9268        }
9269
9270      if (logging != MagickFalse)
9271        {
9272          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9273             "      image->colors=%d", (int) image->colors);
9274
9275          if (image->colormap != NULL)
9276            {
9277              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9278                  "       i     (red,green,blue,alpha)");
9279
9280              for (i=0; i < (ssize_t) image->colors; i++)
9281              {
9282                if (i < 300 || i >= (ssize_t) image->colors - 10)
9283                  {
9284                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9285                        "       %d     (%d,%d,%d,%d)",
9286                         (int) i,
9287                         (int) image->colormap[i].red,
9288                         (int) image->colormap[i].green,
9289                         (int) image->colormap[i].blue,
9290                         (int) image->colormap[i].alpha);
9291                  }
9292              }
9293            }
9294
9295            if (number_transparent < 257)
9296              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9297                    "      number_transparent     = %d",
9298                    number_transparent);
9299            else
9300
9301              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9302                    "      number_transparent     > 256");
9303
9304            if (number_opaque < 257)
9305              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306                    "      number_opaque          = %d",
9307                    number_opaque);
9308
9309            else
9310              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9311                    "      number_opaque          > 256");
9312
9313            if (number_semitransparent < 257)
9314              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9315                    "      number_semitransparent = %d",
9316                    number_semitransparent);
9317
9318            else
9319              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9320                    "      number_semitransparent > 256");
9321
9322            if (ping_have_non_bw == MagickFalse)
9323               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9324                     "      All pixels and the background are black or white");
9325
9326            else if (ping_have_color == MagickFalse)
9327               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9328                     "      All pixels and the background are gray");
9329
9330            else
9331               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9332                     "      At least one pixel or the background is non-gray");
9333
9334            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9335                "    Exit BUILD_PALETTE:");
9336        }
9337
9338    if (mng_info->write_png8 == MagickFalse)
9339       break;
9340
9341    /* Make any reductions necessary for the PNG8 format */
9342     if (image_colors <= 256 &&
9343         image_colors != 0 && image->colormap != NULL &&
9344         number_semitransparent == 0 &&
9345         number_transparent <= 1)
9346       break;
9347
9348     /* PNG8 can't have semitransparent colors so we threshold the
9349      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9350      * transparent color so if more than one is transparent we merge
9351      * them into image->background_color.
9352      */
9353     if (number_semitransparent != 0 || number_transparent > 1)
9354       {
9355         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9356             "    Thresholding the alpha channel to binary");
9357
9358         for (y=0; y < (ssize_t) image->rows; y++)
9359         {
9360           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9361
9362           if (r == (Quantum *) NULL)
9363             break;
9364
9365           for (x=0; x < (ssize_t) image->columns; x++)
9366           {
9367               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9368                 {
9369                   SetPixelViaPixelInfo(image,&image->background_color,r);
9370                   SetPixelAlpha(image,TransparentAlpha,r);
9371                 }
9372               else
9373                   SetPixelAlpha(image,OpaqueAlpha,r);
9374               r+=GetPixelChannels(image);
9375           }
9376
9377           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9378              break;
9379
9380           if (image_colors != 0 && image_colors <= 256 &&
9381              image->colormap != NULL)
9382             for (i=0; i<image_colors; i++)
9383                 image->colormap[i].alpha =
9384                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9385                     TransparentAlpha : OpaqueAlpha);
9386         }
9387       continue;
9388     }
9389
9390     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9391      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9392      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9393      * colors or less.
9394      */
9395     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9396       {
9397         if (logging != MagickFalse)
9398            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9399                "    Quantizing the background color to 4-4-4");
9400
9401         tried_444 = MagickTrue;
9402
9403         LBR04PacketRGB(image->background_color);
9404
9405         if (logging != MagickFalse)
9406           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9407               "    Quantizing the pixel colors to 4-4-4");
9408
9409         if (image->colormap == NULL)
9410         {
9411           for (y=0; y < (ssize_t) image->rows; y++)
9412           {
9413             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9414
9415             if (r == (Quantum *) NULL)
9416               break;
9417
9418             for (x=0; x < (ssize_t) image->columns; x++)
9419             {
9420               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9421                   LBR04PixelRGB(r);
9422               r+=GetPixelChannels(image);
9423             }
9424
9425             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9426                break;
9427           }
9428         }
9429
9430         else /* Should not reach this; colormap already exists and
9431                 must be <= 256 */
9432         {
9433           if (logging != MagickFalse)
9434               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9435               "    Quantizing the colormap to 4-4-4");
9436
9437           for (i=0; i<image_colors; i++)
9438           {
9439             LBR04PacketRGB(image->colormap[i]);
9440           }
9441         }
9442         continue;
9443       }
9444
9445     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9446       {
9447         if (logging != MagickFalse)
9448            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9449                "    Quantizing the background color to 3-3-3");
9450
9451         tried_333 = MagickTrue;
9452
9453         LBR03PacketRGB(image->background_color);
9454
9455         if (logging != MagickFalse)
9456           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9457               "    Quantizing the pixel colors to 3-3-3-1");
9458
9459         if (image->colormap == NULL)
9460         {
9461           for (y=0; y < (ssize_t) image->rows; y++)
9462           {
9463             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9464
9465             if (r == (Quantum *) NULL)
9466               break;
9467
9468             for (x=0; x < (ssize_t) image->columns; x++)
9469             {
9470               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9471                   LBR03RGB(r);
9472               r+=GetPixelChannels(image);
9473             }
9474
9475             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9476                break;
9477           }
9478         }
9479
9480         else /* Should not reach this; colormap already exists and
9481                 must be <= 256 */
9482         {
9483           if (logging != MagickFalse)
9484               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9485               "    Quantizing the colormap to 3-3-3-1");
9486           for (i=0; i<image_colors; i++)
9487           {
9488               LBR03PacketRGB(image->colormap[i]);
9489           }
9490         }
9491         continue;
9492       }
9493
9494     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9495       {
9496         if (logging != MagickFalse)
9497            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9498                "    Quantizing the background color to 3-3-2");
9499
9500         tried_332 = MagickTrue;
9501
9502         /* Red and green were already done so we only quantize the blue
9503          * channel
9504          */
9505
9506         LBR02PacketBlue(image->background_color);
9507
9508         if (logging != MagickFalse)
9509           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9510               "    Quantizing the pixel colors to 3-3-2-1");
9511
9512         if (image->colormap == NULL)
9513         {
9514           for (y=0; y < (ssize_t) image->rows; y++)
9515           {
9516             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9517
9518             if (r == (Quantum *) NULL)
9519               break;
9520
9521             for (x=0; x < (ssize_t) image->columns; x++)
9522             {
9523               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9524                   LBR02PixelBlue(r);
9525               r+=GetPixelChannels(image);
9526             }
9527
9528             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9529                break;
9530           }
9531         }
9532
9533         else /* Should not reach this; colormap already exists and
9534                 must be <= 256 */
9535         {
9536           if (logging != MagickFalse)
9537               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9538               "    Quantizing the colormap to 3-3-2-1");
9539           for (i=0; i<image_colors; i++)
9540           {
9541               LBR02PacketBlue(image->colormap[i]);
9542           }
9543       }
9544       continue;
9545     }
9546
9547     if (image_colors == 0 || image_colors > 256)
9548     {
9549       /* Take care of special case with 256 opaque colors + 1 transparent
9550        * color.  We don't need to quantize to 2-3-2-1; we only need to
9551        * eliminate one color, so we'll merge the two darkest red
9552        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9553        */
9554       if (logging != MagickFalse)
9555         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9556             "    Merging two dark red background colors to 3-3-2-1");
9557
9558       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9559           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9560           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9561       {
9562          image->background_color.red=ScaleCharToQuantum(0x24);
9563       }
9564
9565       if (logging != MagickFalse)
9566         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9567             "    Merging two dark red pixel colors to 3-3-2-1");
9568
9569       if (image->colormap == NULL)
9570       {
9571         for (y=0; y < (ssize_t) image->rows; y++)
9572         {
9573           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9574
9575           if (r == (Quantum *) NULL)
9576             break;
9577
9578           for (x=0; x < (ssize_t) image->columns; x++)
9579           {
9580             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9581                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9582                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9583                 GetPixelAlpha(image,r) == OpaqueAlpha)
9584               {
9585                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9586               }
9587             r+=GetPixelChannels(image);
9588           }
9589
9590           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9591              break;
9592
9593         }
9594       }
9595
9596       else
9597       {
9598          for (i=0; i<image_colors; i++)
9599          {
9600             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9601                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9602                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9603             {
9604                image->colormap[i].red=ScaleCharToQuantum(0x24);
9605             }
9606          }
9607       }
9608     }
9609   }
9610   }
9611   /* END OF BUILD_PALETTE */
9612
9613   /* If we are excluding the tRNS chunk and there is transparency,
9614    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9615    * PNG.
9616    */
9617   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9618      (number_transparent != 0 || number_semitransparent != 0))
9619     {
9620       unsigned int colortype=mng_info->write_png_colortype;
9621
9622       if (ping_have_color == MagickFalse)
9623         mng_info->write_png_colortype = 5;
9624
9625       else
9626         mng_info->write_png_colortype = 7;
9627
9628       if (colortype != 0 &&
9629          mng_info->write_png_colortype != colortype)
9630         ping_need_colortype_warning=MagickTrue;
9631
9632     }
9633
9634   /* See if cheap transparency is possible.  It is only possible
9635    * when there is a single transparent color, no semitransparent
9636    * color, and no opaque color that has the same RGB components
9637    * as the transparent color.  We only need this information if
9638    * we are writing a PNG with colortype 0 or 2, and we have not
9639    * excluded the tRNS chunk.
9640    */
9641   if (number_transparent == 1 &&
9642       mng_info->write_png_colortype < 4)
9643     {
9644        ping_have_cheap_transparency = MagickTrue;
9645
9646        if (number_semitransparent != 0)
9647          ping_have_cheap_transparency = MagickFalse;
9648
9649        else if (image_colors == 0 || image_colors > 256 ||
9650            image->colormap == NULL)
9651          {
9652            register const Quantum
9653              *q;
9654
9655            for (y=0; y < (ssize_t) image->rows; y++)
9656            {
9657              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9658
9659              if (q == (Quantum *) NULL)
9660                break;
9661
9662              for (x=0; x < (ssize_t) image->columns; x++)
9663              {
9664                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9665                      (unsigned short) GetPixelRed(image,q) ==
9666                                      ping_trans_color.red &&
9667                      (unsigned short) GetPixelGreen(image,q) ==
9668                                      ping_trans_color.green &&
9669                      (unsigned short) GetPixelBlue(image,q) ==
9670                                      ping_trans_color.blue)
9671                    {
9672                      ping_have_cheap_transparency = MagickFalse;
9673                      break;
9674                    }
9675
9676                  q+=GetPixelChannels(image);
9677              }
9678
9679              if (ping_have_cheap_transparency == MagickFalse)
9680                 break;
9681            }
9682          }
9683        else
9684          {
9685             /* Assuming that image->colormap[0] is the one transparent color
9686              * and that all others are opaque.
9687              */
9688             if (image_colors > 1)
9689               for (i=1; i<image_colors; i++)
9690                 if (image->colormap[i].red == image->colormap[0].red &&
9691                     image->colormap[i].green == image->colormap[0].green &&
9692                     image->colormap[i].blue == image->colormap[0].blue)
9693                   {
9694                      ping_have_cheap_transparency = MagickFalse;
9695                      break;
9696                   }
9697          }
9698
9699        if (logging != MagickFalse)
9700          {
9701            if (ping_have_cheap_transparency == MagickFalse)
9702              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9703                  "   Cheap transparency is not possible.");
9704
9705            else
9706              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9707                  "   Cheap transparency is possible.");
9708          }
9709      }
9710   else
9711     ping_have_cheap_transparency = MagickFalse;
9712
9713   image_depth=image->depth;
9714
9715   quantum_info = (QuantumInfo *) NULL;
9716   number_colors=0;
9717   image_colors=(int) image->colors;
9718   image_matte=image->alpha_trait !=
9719         UndefinedPixelTrait ? MagickTrue : MagickFalse;
9720
9721   if (mng_info->write_png_colortype < 5)
9722     mng_info->IsPalette=image->storage_class == PseudoClass &&
9723       image_colors <= 256 && image->colormap != NULL;
9724   else
9725     mng_info->IsPalette = MagickFalse;
9726
9727   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9728      (image->colors == 0 || image->colormap == NULL))
9729     {
9730       image_info=DestroyImageInfo(image_info);
9731       image=DestroyImage(image);
9732       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9733           "Cannot write PNG8 or color-type 3; colormap is NULL",
9734           "`%s'",IMimage->filename);
9735       return(MagickFalse);
9736     }
9737
9738   /*
9739     Allocate the PNG structures
9740   */
9741 #ifdef PNG_USER_MEM_SUPPORTED
9742  error_info.image=image;
9743  error_info.exception=exception;
9744   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9745     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9746     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9747
9748 #else
9749   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9750     MagickPNGErrorHandler,MagickPNGWarningHandler);
9751
9752 #endif
9753   if (ping == (png_struct *) NULL)
9754     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9755
9756   ping_info=png_create_info_struct(ping);
9757
9758   if (ping_info == (png_info *) NULL)
9759     {
9760       png_destroy_write_struct(&ping,(png_info **) NULL);
9761       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9762     }
9763
9764   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9765   pixel_info=(MemoryInfo *) NULL;
9766
9767   if (setjmp(png_jmpbuf(ping)))
9768     {
9769       /*
9770         PNG write failed.
9771       */
9772 #ifdef PNG_DEBUG
9773      if (image_info->verbose)
9774         (void) printf("PNG write has failed.\n");
9775 #endif
9776       png_destroy_write_struct(&ping,&ping_info);
9777 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9778       UnlockSemaphoreInfo(ping_semaphore);
9779 #endif
9780
9781       if (pixel_info != (MemoryInfo *) NULL)
9782         pixel_info=RelinquishVirtualMemory(pixel_info);
9783
9784       if (quantum_info != (QuantumInfo *) NULL)
9785         quantum_info=DestroyQuantumInfo(quantum_info);
9786
9787       if (ping_have_blob != MagickFalse)
9788           (void) CloseBlob(image);
9789       image_info=DestroyImageInfo(image_info);
9790       image=DestroyImage(image);
9791       return(MagickFalse);
9792     }
9793
9794   /* {  For navigation to end of SETJMP-protected block.  Within this
9795    *    block, use png_error() instead of Throwing an Exception, to ensure
9796    *    that libpng is able to clean up, and that the semaphore is unlocked.
9797    */
9798
9799 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9800   LockSemaphoreInfo(ping_semaphore);
9801 #endif
9802
9803 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9804   /* Allow benign errors */
9805   png_set_benign_errors(ping, 1);
9806 #endif
9807
9808 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9809   /* Reject images with too many rows or columns */
9810   png_set_user_limits(ping,
9811     (png_uint_32) MagickMin(0x7fffffffL,
9812         GetMagickResourceLimit(WidthResource)),
9813     (png_uint_32) MagickMin(0x7fffffffL,
9814         GetMagickResourceLimit(HeightResource)));
9815 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9816
9817   /*
9818     Prepare PNG for writing.
9819   */
9820
9821 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9822   if (mng_info->write_mng)
9823   {
9824      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9825 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9826      /* Disable new libpng-1.5.10 feature when writing a MNG because
9827       * zero-length PLTE is OK
9828       */
9829      png_set_check_for_invalid_index (ping, 0);
9830 # endif
9831   }
9832
9833 #else
9834 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9835   if (mng_info->write_mng)
9836      png_permit_empty_plte(ping,MagickTrue);
9837
9838 # endif
9839 #endif
9840
9841   x=0;
9842
9843   ping_width=(png_uint_32) image->columns;
9844   ping_height=(png_uint_32) image->rows;
9845
9846   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9847      image_depth=8;
9848
9849   if (mng_info->write_png48 || mng_info->write_png64)
9850      image_depth=16;
9851
9852   if (mng_info->write_png_depth != 0)
9853      image_depth=mng_info->write_png_depth;
9854
9855   /* Adjust requested depth to next higher valid depth if necessary */
9856   if (image_depth > 8)
9857      image_depth=16;
9858
9859   if ((image_depth > 4) && (image_depth < 8))
9860      image_depth=8;
9861
9862   if (image_depth == 3)
9863      image_depth=4;
9864
9865   if (logging != MagickFalse)
9866     {
9867      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9868         "    width=%.20g",(double) ping_width);
9869      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9870         "    height=%.20g",(double) ping_height);
9871      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9872         "    image_matte=%.20g",(double) image->alpha_trait);
9873      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9874         "    image->depth=%.20g",(double) image->depth);
9875      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9876         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9877     }
9878
9879   save_image_depth=image_depth;
9880   ping_bit_depth=(png_byte) save_image_depth;
9881
9882
9883 #if defined(PNG_pHYs_SUPPORTED)
9884   if (ping_exclude_pHYs == MagickFalse)
9885   {
9886   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9887       (!mng_info->write_mng || !mng_info->equal_physs))
9888     {
9889       if (logging != MagickFalse)
9890         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9891             "    Setting up pHYs chunk");
9892
9893       if (image->units == PixelsPerInchResolution)
9894         {
9895           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9896           ping_pHYs_x_resolution=
9897              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9898           ping_pHYs_y_resolution=
9899              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9900         }
9901
9902       else if (image->units == PixelsPerCentimeterResolution)
9903         {
9904           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9905           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9906           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9907         }
9908
9909       else
9910         {
9911           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9912           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9913           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9914         }
9915
9916       if (logging != MagickFalse)
9917         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9918           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9919           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9920           (int) ping_pHYs_unit_type);
9921        ping_have_pHYs = MagickTrue;
9922     }
9923   }
9924 #endif
9925
9926   if (ping_exclude_bKGD == MagickFalse)
9927   {
9928   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9929     {
9930        unsigned int
9931          mask;
9932
9933        mask=0xffff;
9934        if (ping_bit_depth == 8)
9935           mask=0x00ff;
9936
9937        if (ping_bit_depth == 4)
9938           mask=0x000f;
9939
9940        if (ping_bit_depth == 2)
9941           mask=0x0003;
9942
9943        if (ping_bit_depth == 1)
9944           mask=0x0001;
9945
9946        ping_background.red=(png_uint_16)
9947          (ScaleQuantumToShort(image->background_color.red) & mask);
9948
9949        ping_background.green=(png_uint_16)
9950          (ScaleQuantumToShort(image->background_color.green) & mask);
9951
9952        ping_background.blue=(png_uint_16)
9953          (ScaleQuantumToShort(image->background_color.blue) & mask);
9954
9955        ping_background.gray=(png_uint_16) ping_background.green;
9956     }
9957
9958   if (logging != MagickFalse)
9959     {
9960       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9961           "    Setting up bKGD chunk (1)");
9962       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9963           "      background_color index is %d",
9964           (int) ping_background.index);
9965
9966       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9967           "    ping_bit_depth=%d",ping_bit_depth);
9968     }
9969
9970   ping_have_bKGD = MagickTrue;
9971   }
9972
9973   /*
9974     Select the color type.
9975   */
9976   matte=image_matte;
9977   old_bit_depth=0;
9978
9979   if (mng_info->IsPalette && mng_info->write_png8)
9980     {
9981       /* To do: make this a function cause it's used twice, except
9982          for reducing the sample depth from 8. */
9983
9984       number_colors=image_colors;
9985
9986       ping_have_tRNS=MagickFalse;
9987
9988       /*
9989         Set image palette.
9990       */
9991       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9992
9993       if (logging != MagickFalse)
9994         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9995             "  Setting up PLTE chunk with %d colors (%d)",
9996             number_colors, image_colors);
9997
9998       for (i=0; i < (ssize_t) number_colors; i++)
9999       {
10000         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10001         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10002         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10003         if (logging != MagickFalse)
10004           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10005 #if MAGICKCORE_QUANTUM_DEPTH == 8
10006             "    %3ld (%3d,%3d,%3d)",
10007 #else
10008             "    %5ld (%5d,%5d,%5d)",
10009 #endif
10010             (long) i,palette[i].red,palette[i].green,palette[i].blue);
10011
10012       }
10013
10014       ping_have_PLTE=MagickTrue;
10015       image_depth=ping_bit_depth;
10016       ping_num_trans=0;
10017
10018       if (matte != MagickFalse)
10019       {
10020           /*
10021             Identify which colormap entry is transparent.
10022           */
10023           assert(number_colors <= 256);
10024           assert(image->colormap != NULL);
10025
10026           for (i=0; i < (ssize_t) number_transparent; i++)
10027              ping_trans_alpha[i]=0;
10028
10029
10030           ping_num_trans=(unsigned short) (number_transparent +
10031              number_semitransparent);
10032
10033           if (ping_num_trans == 0)
10034              ping_have_tRNS=MagickFalse;
10035
10036           else
10037              ping_have_tRNS=MagickTrue;
10038       }
10039
10040       if (ping_exclude_bKGD == MagickFalse)
10041       {
10042        /*
10043         * Identify which colormap entry is the background color.
10044         */
10045
10046         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
10047           if (IsPNGColorEqual(ping_background,image->colormap[i]))
10048             break;
10049
10050         ping_background.index=(png_byte) i;
10051
10052         if (logging != MagickFalse)
10053           {
10054             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10055                  "      background_color index is %d",
10056                  (int) ping_background.index);
10057           }
10058       }
10059     } /* end of write_png8 */
10060
10061   else if (mng_info->write_png_colortype == 1)
10062     {
10063       image_matte=MagickFalse;
10064       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10065     }
10066
10067   else if (mng_info->write_png24 || mng_info->write_png48 ||
10068       mng_info->write_png_colortype == 3)
10069     {
10070       image_matte=MagickFalse;
10071       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10072     }
10073
10074   else if (mng_info->write_png32 || mng_info->write_png64 ||
10075       mng_info->write_png_colortype == 7)
10076     {
10077       image_matte=MagickTrue;
10078       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10079     }
10080
10081   else /* mng_info->write_pngNN not specified */
10082     {
10083       image_depth=ping_bit_depth;
10084
10085       if (mng_info->write_png_colortype != 0)
10086         {
10087           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
10088
10089           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10090               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10091             image_matte=MagickTrue;
10092
10093           else
10094             image_matte=MagickFalse;
10095
10096           if (logging != MagickFalse)
10097              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10098              "   PNG colortype %d was specified:",(int) ping_color_type);
10099         }
10100
10101       else /* write_png_colortype not specified */
10102         {
10103           if (logging != MagickFalse)
10104              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10105              "  Selecting PNG colortype:");
10106
10107           ping_color_type=(png_byte) ((matte != MagickFalse)?
10108             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
10109
10110           if (image_info->type == TrueColorType)
10111             {
10112               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10113               image_matte=MagickFalse;
10114             }
10115
10116           if (image_info->type == TrueColorAlphaType)
10117             {
10118               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
10119               image_matte=MagickTrue;
10120             }
10121
10122           if (image_info->type == PaletteType ||
10123               image_info->type == PaletteAlphaType)
10124             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10125
10126           if (mng_info->write_png_colortype == 0 &&
10127              image_info->type == UndefinedType)
10128             {
10129               if (ping_have_color == MagickFalse)
10130                 {
10131                   if (image_matte == MagickFalse)
10132                     {
10133                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
10134                       image_matte=MagickFalse;
10135                     }
10136
10137                   else
10138                     {
10139                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
10140                       image_matte=MagickTrue;
10141                     }
10142                 }
10143               else
10144                 {
10145                   if (image_matte == MagickFalse)
10146                     {
10147                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
10148                       image_matte=MagickFalse;
10149                     }
10150
10151                   else
10152                     {
10153                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
10154                       image_matte=MagickTrue;
10155                     }
10156                  }
10157             }
10158
10159         }
10160
10161       if (logging != MagickFalse)
10162          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10163          "    Selected PNG colortype=%d",ping_color_type);
10164
10165       if (ping_bit_depth < 8)
10166         {
10167           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
10168               ping_color_type == PNG_COLOR_TYPE_RGB ||
10169               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
10170             ping_bit_depth=8;
10171         }
10172
10173       old_bit_depth=ping_bit_depth;
10174
10175       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10176         {
10177           if (image->alpha_trait == UndefinedPixelTrait &&
10178                ping_have_non_bw == MagickFalse)
10179              ping_bit_depth=1;
10180         }
10181
10182       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10183         {
10184            size_t one = 1;
10185            ping_bit_depth=1;
10186
10187            if (image->colors == 0)
10188            {
10189               /* DO SOMETHING */
10190                 png_error(ping,"image has 0 colors");
10191            }
10192
10193            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10194              ping_bit_depth <<= 1;
10195         }
10196
10197       if (logging != MagickFalse)
10198          {
10199            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10200             "    Number of colors: %.20g",(double) image_colors);
10201
10202            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10203             "    Tentative PNG bit depth: %d",ping_bit_depth);
10204          }
10205
10206       if (ping_bit_depth < (int) mng_info->write_png_depth)
10207          ping_bit_depth = mng_info->write_png_depth;
10208     }
10209
10210   image_depth=ping_bit_depth;
10211
10212   if (logging != MagickFalse)
10213     {
10214       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10215         "    Tentative PNG color type: %s (%.20g)",
10216         PngColorTypeToString(ping_color_type),
10217         (double) ping_color_type);
10218
10219       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10220         "    image_info->type: %.20g",(double) image_info->type);
10221
10222       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10223         "    image_depth: %.20g",(double) image_depth);
10224
10225       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10226
10227         "    image->depth: %.20g",(double) image->depth);
10228
10229       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10230         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
10231     }
10232
10233   if (matte != MagickFalse)
10234     {
10235       if (mng_info->IsPalette)
10236         {
10237           if (mng_info->write_png_colortype == 0)
10238             {
10239               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10240
10241               if (ping_have_color != MagickFalse)
10242                  ping_color_type=PNG_COLOR_TYPE_RGBA;
10243             }
10244
10245           /*
10246            * Determine if there is any transparent color.
10247           */
10248           if (number_transparent + number_semitransparent == 0)
10249             {
10250               /*
10251                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
10252               */
10253
10254               image_matte=MagickFalse;
10255
10256               if (mng_info->write_png_colortype == 0)
10257                 ping_color_type&=0x03;
10258             }
10259
10260           else
10261             {
10262               unsigned int
10263                 mask;
10264
10265               mask=0xffff;
10266
10267               if (ping_bit_depth == 8)
10268                  mask=0x00ff;
10269
10270               if (ping_bit_depth == 4)
10271                  mask=0x000f;
10272
10273               if (ping_bit_depth == 2)
10274                  mask=0x0003;
10275
10276               if (ping_bit_depth == 1)
10277                  mask=0x0001;
10278
10279               ping_trans_color.red=(png_uint_16)
10280                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10281
10282               ping_trans_color.green=(png_uint_16)
10283                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10284
10285               ping_trans_color.blue=(png_uint_16)
10286                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10287
10288               ping_trans_color.gray=(png_uint_16)
10289                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10290                    image->colormap)) & mask);
10291
10292               ping_trans_color.index=(png_byte) 0;
10293
10294               ping_have_tRNS=MagickTrue;
10295             }
10296
10297           if (ping_have_tRNS != MagickFalse)
10298             {
10299               /*
10300                * Determine if there is one and only one transparent color
10301                * and if so if it is fully transparent.
10302                */
10303               if (ping_have_cheap_transparency == MagickFalse)
10304                 ping_have_tRNS=MagickFalse;
10305             }
10306
10307           if (ping_have_tRNS != MagickFalse)
10308             {
10309               if (mng_info->write_png_colortype == 0)
10310                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10311
10312               if (image_depth == 8)
10313                 {
10314                   ping_trans_color.red&=0xff;
10315                   ping_trans_color.green&=0xff;
10316                   ping_trans_color.blue&=0xff;
10317                   ping_trans_color.gray&=0xff;
10318                 }
10319             }
10320         }
10321       else
10322         {
10323           if (image_depth == 8)
10324             {
10325               ping_trans_color.red&=0xff;
10326               ping_trans_color.green&=0xff;
10327               ping_trans_color.blue&=0xff;
10328               ping_trans_color.gray&=0xff;
10329             }
10330         }
10331     }
10332
10333     matte=image_matte;
10334
10335     if (ping_have_tRNS != MagickFalse)
10336       image_matte=MagickFalse;
10337
10338     if ((mng_info->IsPalette) &&
10339         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10340         ping_have_color == MagickFalse &&
10341         (image_matte == MagickFalse || image_depth >= 8))
10342       {
10343         size_t one=1;
10344
10345         if (image_matte != MagickFalse)
10346           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10347
10348         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10349           {
10350             ping_color_type=PNG_COLOR_TYPE_GRAY;
10351
10352             if (save_image_depth == 16 && image_depth == 8)
10353               {
10354                 if (logging != MagickFalse)
10355                   {
10356                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10357                         "  Scaling ping_trans_color (0)");
10358                   }
10359                     ping_trans_color.gray*=0x0101;
10360               }
10361           }
10362
10363         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10364           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10365
10366         if ((image_colors == 0) ||
10367              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10368           image_colors=(int) (one << image_depth);
10369
10370         if (image_depth > 8)
10371           ping_bit_depth=16;
10372
10373         else
10374           {
10375             ping_bit_depth=8;
10376             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10377               {
10378                 if(!mng_info->write_png_depth)
10379                   {
10380                     ping_bit_depth=1;
10381
10382                     while ((int) (one << ping_bit_depth)
10383                         < (ssize_t) image_colors)
10384                       ping_bit_depth <<= 1;
10385                   }
10386               }
10387
10388             else if (ping_color_type ==
10389                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10390                 mng_info->IsPalette)
10391               {
10392               /* Check if grayscale is reducible */
10393
10394                 int
10395                   depth_4_ok=MagickTrue,
10396                   depth_2_ok=MagickTrue,
10397                   depth_1_ok=MagickTrue;
10398
10399                 for (i=0; i < (ssize_t) image_colors; i++)
10400                 {
10401                    unsigned char
10402                      intensity;
10403
10404                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10405
10406                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10407                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10408                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10409                      depth_2_ok=depth_1_ok=MagickFalse;
10410                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10411                      depth_1_ok=MagickFalse;
10412                 }
10413
10414                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10415                   ping_bit_depth=1;
10416
10417                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10418                   ping_bit_depth=2;
10419
10420                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10421                   ping_bit_depth=4;
10422               }
10423           }
10424
10425           image_depth=ping_bit_depth;
10426       }
10427
10428     else
10429
10430       if (mng_info->IsPalette)
10431       {
10432         number_colors=image_colors;
10433
10434         if (image_depth <= 8)
10435           {
10436             /*
10437               Set image palette.
10438             */
10439             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10440
10441             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10442               {
10443                 for (i=0; i < (ssize_t) number_colors; i++)
10444                 {
10445                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10446                   palette[i].green=
10447                     ScaleQuantumToChar(image->colormap[i].green);
10448                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10449                 }
10450
10451                 if (logging != MagickFalse)
10452                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10453                     "  Setting up PLTE chunk with %d colors",
10454                     number_colors);
10455
10456                 ping_have_PLTE=MagickTrue;
10457               }
10458
10459             /* color_type is PNG_COLOR_TYPE_PALETTE */
10460             if (mng_info->write_png_depth == 0)
10461               {
10462                 size_t
10463                   one;
10464
10465                 ping_bit_depth=1;
10466                 one=1;
10467
10468                 while ((one << ping_bit_depth) < (size_t) number_colors)
10469                   ping_bit_depth <<= 1;
10470               }
10471
10472             ping_num_trans=0;
10473
10474             if (matte != MagickFalse)
10475               {
10476                 /*
10477                  * Set up trans_colors array.
10478                  */
10479                 assert(number_colors <= 256);
10480
10481                 ping_num_trans=(unsigned short) (number_transparent +
10482                   number_semitransparent);
10483
10484                 if (ping_num_trans == 0)
10485                   ping_have_tRNS=MagickFalse;
10486
10487                 else
10488                   {
10489                     if (logging != MagickFalse)
10490                       {
10491                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10492                           "  Scaling ping_trans_color (1)");
10493                       }
10494                     ping_have_tRNS=MagickTrue;
10495
10496                     for (i=0; i < ping_num_trans; i++)
10497                     {
10498                        ping_trans_alpha[i]= (png_byte)
10499                          ScaleQuantumToChar(image->colormap[i].alpha);
10500                     }
10501                   }
10502               }
10503           }
10504       }
10505
10506     else
10507       {
10508
10509         if (image_depth < 8)
10510           image_depth=8;
10511
10512         if ((save_image_depth == 16) && (image_depth == 8))
10513           {
10514             if (logging != MagickFalse)
10515               {
10516                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10517                   "    Scaling ping_trans_color from (%d,%d,%d)",
10518                   (int) ping_trans_color.red,
10519                   (int) ping_trans_color.green,
10520                   (int) ping_trans_color.blue);
10521               }
10522
10523             ping_trans_color.red*=0x0101;
10524             ping_trans_color.green*=0x0101;
10525             ping_trans_color.blue*=0x0101;
10526             ping_trans_color.gray*=0x0101;
10527
10528             if (logging != MagickFalse)
10529               {
10530                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10531                   "    to (%d,%d,%d)",
10532                   (int) ping_trans_color.red,
10533                   (int) ping_trans_color.green,
10534                   (int) ping_trans_color.blue);
10535               }
10536           }
10537       }
10538
10539     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10540          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10541
10542     /*
10543       Adjust background and transparency samples in sub-8-bit grayscale files.
10544     */
10545     if (ping_bit_depth < 8 && ping_color_type ==
10546         PNG_COLOR_TYPE_GRAY)
10547       {
10548          png_uint_16
10549            maxval;
10550
10551          size_t
10552            one=1;
10553
10554          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10555
10556          if (ping_exclude_bKGD == MagickFalse)
10557          {
10558
10559          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10560            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10561            &image->background_color))) +.5)));
10562
10563          if (logging != MagickFalse)
10564            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10565              "  Setting up bKGD chunk (2)");
10566          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10567              "      background_color index is %d",
10568              (int) ping_background.index);
10569
10570          ping_have_bKGD = MagickTrue;
10571          }
10572
10573          if (logging != MagickFalse)
10574            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10575              "  Scaling ping_trans_color.gray from %d",
10576              (int)ping_trans_color.gray);
10577
10578          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10579            ping_trans_color.gray)+.5);
10580
10581          if (logging != MagickFalse)
10582            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10583              "      to %d", (int)ping_trans_color.gray);
10584       }
10585
10586   if (ping_exclude_bKGD == MagickFalse)
10587   {
10588     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10589       {
10590         /*
10591            Identify which colormap entry is the background color.
10592         */
10593
10594         number_colors=image_colors;
10595
10596         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10597           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10598             break;
10599
10600         ping_background.index=(png_byte) i;
10601
10602         if (logging != MagickFalse)
10603           {
10604             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10605               "  Setting up bKGD chunk with index=%d",(int) i);
10606           }
10607
10608         if (i < (ssize_t) number_colors)
10609           {
10610             ping_have_bKGD = MagickTrue;
10611
10612             if (logging != MagickFalse)
10613               {
10614                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10615                   "     background   =(%d,%d,%d)",
10616                         (int) ping_background.red,
10617                         (int) ping_background.green,
10618                         (int) ping_background.blue);
10619               }
10620           }
10621
10622         else  /* Can't happen */
10623           {
10624             if (logging != MagickFalse)
10625               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10626                   "      No room in PLTE to add bKGD color");
10627             ping_have_bKGD = MagickFalse;
10628           }
10629       }
10630   }
10631
10632   if (logging != MagickFalse)
10633     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10634       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10635       ping_color_type);
10636   /*
10637     Initialize compression level and filtering.
10638   */
10639   if (logging != MagickFalse)
10640     {
10641       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10642         "  Setting up deflate compression");
10643
10644       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10645         "    Compression buffer size: 32768");
10646     }
10647
10648   png_set_compression_buffer_size(ping,32768L);
10649
10650   if (logging != MagickFalse)
10651     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652       "    Compression mem level: 9");
10653
10654   png_set_compression_mem_level(ping, 9);
10655
10656   /* Untangle the "-quality" setting:
10657
10658      Undefined is 0; the default is used.
10659      Default is 75
10660
10661      10's digit:
10662
10663         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10664            zlib default compression level
10665
10666         1-9: the zlib compression level
10667
10668      1's digit:
10669
10670         0-4: the PNG filter method
10671
10672         5:   libpng adaptive filtering if compression level > 5
10673              libpng filter type "none" if compression level <= 5
10674                 or if image is grayscale or palette
10675
10676         6:   libpng adaptive filtering
10677
10678         7:   "LOCO" filtering (intrapixel differing) if writing
10679              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10680              and earlier because of a missing "else".
10681
10682         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10683              filtering. Unused prior to IM-6.7.0-10, was same as 6
10684
10685         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10686              Unused prior to IM-6.7.0-10, was same as 6
10687
10688     Note that using the -quality option, not all combinations of
10689     PNG filter type, zlib compression level, and zlib compression
10690     strategy are possible.  This will be addressed soon in a
10691     release that accomodates "-define png:compression-strategy", etc.
10692
10693    */
10694
10695   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10696      image_info->quality;
10697
10698   if (quality <= 9)
10699     {
10700       if (mng_info->write_png_compression_strategy == 0)
10701         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10702     }
10703
10704   else if (mng_info->write_png_compression_level == 0)
10705     {
10706       int
10707         level;
10708
10709       level=(int) MagickMin((ssize_t) quality/10,9);
10710
10711       mng_info->write_png_compression_level = level+1;
10712     }
10713
10714   if (mng_info->write_png_compression_strategy == 0)
10715     {
10716         if ((quality %10) == 8 || (quality %10) == 9)
10717 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10718           mng_info->write_png_compression_strategy=Z_RLE+1;
10719 #else
10720           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10721 #endif
10722     }
10723
10724   if (mng_info->write_png_compression_filter == 0)
10725         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10726
10727   if (logging != MagickFalse)
10728     {
10729         if (mng_info->write_png_compression_level)
10730           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10731             "    Compression level:    %d",
10732             (int) mng_info->write_png_compression_level-1);
10733
10734         if (mng_info->write_png_compression_strategy)
10735           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10736             "    Compression strategy: %d",
10737             (int) mng_info->write_png_compression_strategy-1);
10738
10739         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10740           "  Setting up filtering");
10741
10742         if (mng_info->write_png_compression_filter == 6)
10743           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10744             "    Base filter method: ADAPTIVE");
10745         else if (mng_info->write_png_compression_filter == 0 ||
10746                  mng_info->write_png_compression_filter == 1)
10747           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10748             "    Base filter method: NONE");
10749         else
10750           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10751             "    Base filter method: %d",
10752             (int) mng_info->write_png_compression_filter-1);
10753     }
10754
10755   if (mng_info->write_png_compression_level != 0)
10756     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10757
10758   if (mng_info->write_png_compression_filter == 6)
10759     {
10760       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10761          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10762          (quality < 50))
10763         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10764       else
10765         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10766      }
10767   else if (mng_info->write_png_compression_filter == 7 ||
10768       mng_info->write_png_compression_filter == 10)
10769     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10770
10771   else if (mng_info->write_png_compression_filter == 8)
10772     {
10773 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10774       if (mng_info->write_mng)
10775       {
10776          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10777              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10778         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10779       }
10780 #endif
10781       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10782     }
10783
10784   else if (mng_info->write_png_compression_filter == 9)
10785     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10786
10787   else if (mng_info->write_png_compression_filter != 0)
10788     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10789        mng_info->write_png_compression_filter-1);
10790
10791   if (mng_info->write_png_compression_strategy != 0)
10792     png_set_compression_strategy(ping,
10793        mng_info->write_png_compression_strategy-1);
10794
10795   ping_interlace_method=image_info->interlace != NoInterlace;
10796
10797   if (mng_info->write_mng)
10798     png_set_sig_bytes(ping,8);
10799
10800   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10801
10802   if (mng_info->write_png_colortype != 0)
10803     {
10804      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10805        if (ping_have_color != MagickFalse)
10806          {
10807            ping_color_type = PNG_COLOR_TYPE_RGB;
10808
10809            if (ping_bit_depth < 8)
10810              ping_bit_depth=8;
10811          }
10812
10813      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10814        if (ping_have_color != MagickFalse)
10815          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10816     }
10817
10818   if (ping_need_colortype_warning != MagickFalse ||
10819      ((mng_info->write_png_depth &&
10820      (int) mng_info->write_png_depth != ping_bit_depth) ||
10821      (mng_info->write_png_colortype &&
10822      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10823       mng_info->write_png_colortype != 7 &&
10824       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10825     {
10826       if (logging != MagickFalse)
10827         {
10828           if (ping_need_colortype_warning != MagickFalse)
10829             {
10830               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10831                  "  Image has transparency but tRNS chunk was excluded");
10832             }
10833
10834           if (mng_info->write_png_depth)
10835             {
10836               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10837                   "  Defined png:bit-depth=%u, Computed depth=%u",
10838                   mng_info->write_png_depth,
10839                   ping_bit_depth);
10840             }
10841
10842           if (mng_info->write_png_colortype)
10843             {
10844               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10845                   "  Defined png:color-type=%u, Computed color type=%u",
10846                   mng_info->write_png_colortype-1,
10847                   ping_color_type);
10848             }
10849         }
10850
10851       png_warning(ping,
10852         "Cannot write image with defined png:bit-depth or png:color-type.");
10853     }
10854
10855   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10856     {
10857       /* Add an opaque matte channel */
10858       image->alpha_trait = BlendPixelTrait;
10859       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10860
10861       if (logging != MagickFalse)
10862         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10863           "  Added an opaque matte channel");
10864     }
10865
10866   if (number_transparent != 0 || number_semitransparent != 0)
10867     {
10868       if (ping_color_type < 4)
10869         {
10870            ping_have_tRNS=MagickTrue;
10871            if (logging != MagickFalse)
10872              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10873                "  Setting ping_have_tRNS=MagickTrue.");
10874         }
10875     }
10876
10877   if (logging != MagickFalse)
10878     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10879       "  Writing PNG header chunks");
10880
10881   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10882                ping_bit_depth,ping_color_type,
10883                ping_interlace_method,ping_compression_method,
10884                ping_filter_method);
10885
10886   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10887     {
10888       png_set_PLTE(ping,ping_info,palette,number_colors);
10889
10890       if (logging != MagickFalse)
10891         {
10892           for (i=0; i< (ssize_t) number_colors; i++)
10893           {
10894             if (i < ping_num_trans)
10895               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10896                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10897                       (int) i,
10898                       (int) palette[i].red,
10899                       (int) palette[i].green,
10900                       (int) palette[i].blue,
10901                       (int) i,
10902                       (int) ping_trans_alpha[i]);
10903              else
10904               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10905                 "     PLTE[%d] = (%d,%d,%d)",
10906                       (int) i,
10907                       (int) palette[i].red,
10908                       (int) palette[i].green,
10909                       (int) palette[i].blue);
10910            }
10911          }
10912     }
10913
10914   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10915   if (ping_exclude_sRGB != MagickFalse ||
10916      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10917   {
10918     if ((ping_exclude_tEXt == MagickFalse ||
10919        ping_exclude_zTXt == MagickFalse) &&
10920        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10921     {
10922       ResetImageProfileIterator(image);
10923       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10924       {
10925         profile=GetImageProfile(image,name);
10926
10927         if (profile != (StringInfo *) NULL)
10928           {
10929 #ifdef PNG_WRITE_iCCP_SUPPORTED
10930             if ((LocaleCompare(name,"ICC") == 0) ||
10931                 (LocaleCompare(name,"ICM") == 0))
10932               {
10933                 ping_have_iCCP = MagickTrue;
10934                 if (ping_exclude_iCCP == MagickFalse)
10935                   {
10936                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10937                         "  Setting up iCCP chunk");
10938
10939                     png_set_iCCP(ping,ping_info,(png_charp) name,0,
10940 #if (PNG_LIBPNG_VER < 10500)
10941                     (png_charp) GetStringInfoDatum(profile),
10942 #else
10943                     (const png_byte *) GetStringInfoDatum(profile),
10944 #endif
10945                     (png_uint_32) GetStringInfoLength(profile));
10946                   }
10947                 else
10948                   {
10949                     /* Do not write hex-encoded ICC chunk */
10950                        name=GetNextImageProfile(image);
10951                        continue;
10952                   }
10953               }
10954 #endif /* WRITE_iCCP */
10955
10956             if (LocaleCompare(name,"exif") == 0)
10957               {
10958                    /* Do not write hex-encoded ICC chunk; we will
10959                       write it later as an eXIf chunk */
10960                    name=GetNextImageProfile(image);
10961                    continue;
10962               }
10963
10964               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10965                  "  Setting up zTXt chunk with uuencoded %s profile",
10966                  name);
10967               Magick_png_write_raw_profile(image_info,ping,ping_info,
10968                 (unsigned char *) name,(unsigned char *) name,
10969                 GetStringInfoDatum(profile),
10970                 (png_uint_32) GetStringInfoLength(profile));
10971           }
10972         name=GetNextImageProfile(image);
10973       }
10974     }
10975   }
10976
10977 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10978   if ((mng_info->have_write_global_srgb == 0) &&
10979       ping_have_iCCP != MagickTrue &&
10980       (ping_have_sRGB != MagickFalse ||
10981       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10982     {
10983       if (ping_exclude_sRGB == MagickFalse)
10984         {
10985           /*
10986             Note image rendering intent.
10987           */
10988           if (logging != MagickFalse)
10989             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10990                 "  Setting up sRGB chunk");
10991
10992           (void) png_set_sRGB(ping,ping_info,(
10993             Magick_RenderingIntent_to_PNG_RenderingIntent(
10994               image->rendering_intent)));
10995
10996           ping_have_sRGB = MagickTrue;
10997         }
10998     }
10999
11000   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
11001 #endif
11002     {
11003       if (ping_exclude_gAMA == MagickFalse &&
11004           ping_have_iCCP == MagickFalse &&
11005           ping_have_sRGB == MagickFalse &&
11006           (ping_exclude_sRGB == MagickFalse ||
11007           (image->gamma < .45 || image->gamma > .46)))
11008       {
11009       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
11010         {
11011           /*
11012             Note image gamma.
11013             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
11014           */
11015           if (logging != MagickFalse)
11016             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11017               "  Setting up gAMA chunk");
11018
11019           png_set_gAMA(ping,ping_info,image->gamma);
11020         }
11021       }
11022
11023       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
11024         {
11025           if ((mng_info->have_write_global_chrm == 0) &&
11026               (image->chromaticity.red_primary.x != 0.0))
11027             {
11028               /*
11029                 Note image chromaticity.
11030                 Note: if cHRM+gAMA == sRGB write sRGB instead.
11031               */
11032                PrimaryInfo
11033                  bp,
11034                  gp,
11035                  rp,
11036                  wp;
11037
11038                wp=image->chromaticity.white_point;
11039                rp=image->chromaticity.red_primary;
11040                gp=image->chromaticity.green_primary;
11041                bp=image->chromaticity.blue_primary;
11042
11043                if (logging != MagickFalse)
11044                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11045                    "  Setting up cHRM chunk");
11046
11047                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
11048                    bp.x,bp.y);
11049            }
11050         }
11051     }
11052
11053   if (ping_exclude_bKGD == MagickFalse)
11054     {
11055       if (ping_have_bKGD != MagickFalse)
11056         {
11057           png_set_bKGD(ping,ping_info,&ping_background);
11058           if (logging != MagickFalse)
11059             {
11060               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11061                    "    Setting up bKGD chunk");
11062               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11063                    "      background color = (%d,%d,%d)",
11064                         (int) ping_background.red,
11065                         (int) ping_background.green,
11066                         (int) ping_background.blue);
11067               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11068                    "      index = %d, gray=%d",
11069                         (int) ping_background.index,
11070                         (int) ping_background.gray);
11071             }
11072          }
11073     }
11074
11075   if (ping_exclude_pHYs == MagickFalse)
11076     {
11077       if (ping_have_pHYs != MagickFalse)
11078         {
11079           png_set_pHYs(ping,ping_info,
11080              ping_pHYs_x_resolution,
11081              ping_pHYs_y_resolution,
11082              ping_pHYs_unit_type);
11083
11084           if (logging != MagickFalse)
11085             {
11086               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11087                    "    Setting up pHYs chunk");
11088               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11089                    "      x_resolution=%lu",
11090                    (unsigned long) ping_pHYs_x_resolution);
11091               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11092                    "      y_resolution=%lu",
11093                    (unsigned long) ping_pHYs_y_resolution);
11094               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11095                    "      unit_type=%lu",
11096                    (unsigned long) ping_pHYs_unit_type);
11097             }
11098         }
11099     }
11100
11101 #if defined(PNG_tIME_SUPPORTED)
11102   if (ping_exclude_tIME == MagickFalse)
11103     {
11104       const char
11105         *timestamp;
11106
11107       if (image->taint == MagickFalse)
11108         {
11109           timestamp=GetImageOption(image_info,"png:tIME");
11110
11111           if (timestamp == (const char *) NULL)
11112             timestamp=GetImageProperty(image,"png:tIME",exception);
11113         }
11114
11115       else
11116         {
11117           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11118              "  Reset tIME in tainted image");
11119
11120           timestamp=GetImageProperty(image,"date:modify",exception);
11121         }
11122
11123       if (timestamp != (const char *) NULL)
11124           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
11125     }
11126 #endif
11127
11128   if (mng_info->need_blob != MagickFalse)
11129   {
11130     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
11131        MagickFalse)
11132        png_error(ping,"WriteBlob Failed");
11133
11134      ping_have_blob=MagickTrue;
11135   }
11136
11137   png_write_info_before_PLTE(ping, ping_info);
11138
11139   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
11140     {
11141       if (logging != MagickFalse)
11142         {
11143           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11144               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
11145         }
11146
11147       if (ping_color_type == 3)
11148          (void) png_set_tRNS(ping, ping_info,
11149                 ping_trans_alpha,
11150                 ping_num_trans,
11151                 NULL);
11152
11153       else
11154         {
11155            (void) png_set_tRNS(ping, ping_info,
11156                   NULL,
11157                   0,
11158                   &ping_trans_color);
11159
11160            if (logging != MagickFalse)
11161              {
11162                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11163                  "     tRNS color   =(%d,%d,%d)",
11164                        (int) ping_trans_color.red,
11165                        (int) ping_trans_color.green,
11166                        (int) ping_trans_color.blue);
11167              }
11168          }
11169     }
11170
11171   png_write_info(ping,ping_info);
11172
11173   /* write orNT if image->orientation is defined */
11174   if (image->orientation != UndefinedOrientation)
11175     {
11176       unsigned char
11177         chunk[6];
11178       (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
11179       PNGType(chunk,mng_orNT);
11180       LogPNGChunk(logging,mng_orNT,1L);
11181       /* PNG uses Exif orientation values */
11182       chunk[4]=Magick_Orientation_to_Exif_Orientation(image->orientation);
11183       (void) WriteBlob(image,5,chunk);
11184       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
11185     }
11186
11187   ping_wrote_caNv = MagickFalse;
11188
11189   /* write caNv chunk */
11190   if (ping_exclude_caNv == MagickFalse)
11191     {
11192       if ((image->page.width != 0 && image->page.width != image->columns) ||
11193           (image->page.height != 0 && image->page.height != image->rows) ||
11194           image->page.x != 0 || image->page.y != 0)
11195         {
11196           unsigned char
11197             chunk[20];
11198
11199           (void) WriteBlobMSBULong(image,16L);  /* data length=8 */
11200           PNGType(chunk,mng_caNv);
11201           LogPNGChunk(logging,mng_caNv,16L);
11202           PNGLong(chunk+4,(png_uint_32) image->page.width);
11203           PNGLong(chunk+8,(png_uint_32) image->page.height);
11204           PNGsLong(chunk+12,(png_int_32) image->page.x);
11205           PNGsLong(chunk+16,(png_int_32) image->page.y);
11206           (void) WriteBlob(image,20,chunk);
11207           (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11208           ping_wrote_caNv = MagickTrue;
11209         }
11210     }
11211
11212 #if defined(PNG_oFFs_SUPPORTED)
11213   if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11214     {
11215       if (image->page.x || image->page.y)
11216         {
11217            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11218               (png_int_32) image->page.y, 0);
11219
11220            if (logging != MagickFalse)
11221              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11222                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
11223                  (int) image->page.x, (int) image->page.y);
11224         }
11225     }
11226 #endif
11227
11228 #if (PNG_LIBPNG_VER == 10206)
11229     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11230 #define PNG_HAVE_IDAT               0x04
11231     ping->mode |= PNG_HAVE_IDAT;
11232 #undef PNG_HAVE_IDAT
11233 #endif
11234
11235   png_set_packing(ping);
11236   /*
11237     Allocate memory.
11238   */
11239   rowbytes=image->columns;
11240   if (image_depth > 8)
11241     rowbytes*=2;
11242   switch (ping_color_type)
11243     {
11244       case PNG_COLOR_TYPE_RGB:
11245         rowbytes*=3;
11246         break;
11247
11248       case PNG_COLOR_TYPE_GRAY_ALPHA:
11249         rowbytes*=2;
11250         break;
11251
11252       case PNG_COLOR_TYPE_RGBA:
11253         rowbytes*=4;
11254         break;
11255
11256       default:
11257         break;
11258     }
11259
11260   if (logging != MagickFalse)
11261     {
11262       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11263         "  Writing PNG image data");
11264
11265       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11266         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11267     }
11268   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11269   if (pixel_info == (MemoryInfo *) NULL)
11270     png_error(ping,"Allocation of memory for pixels failed");
11271   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11272
11273   /*
11274     Initialize image scanlines.
11275   */
11276   quantum_info=AcquireQuantumInfo(image_info,image);
11277   if (quantum_info == (QuantumInfo *) NULL)
11278     png_error(ping,"Memory allocation for quantum_info failed");
11279   quantum_info->format=UndefinedQuantumFormat;
11280   SetQuantumDepth(image,quantum_info,image_depth);
11281   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11282   num_passes=png_set_interlace_handling(ping);
11283
11284   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11285        !mng_info->write_png48 && !mng_info->write_png64 &&
11286        !mng_info->write_png32) &&
11287        (mng_info->IsPalette ||
11288        (image_info->type == BilevelType)) &&
11289        image_matte == MagickFalse &&
11290        ping_have_non_bw == MagickFalse)
11291     {
11292       /* Palette, Bilevel, or Opaque Monochrome */
11293       register const Quantum
11294         *p;
11295
11296       SetQuantumDepth(image,quantum_info,8);
11297       for (pass=0; pass < num_passes; pass++)
11298       {
11299         /*
11300           Convert PseudoClass image to a PNG monochrome image.
11301         */
11302         for (y=0; y < (ssize_t) image->rows; y++)
11303         {
11304           if (logging != MagickFalse && y == 0)
11305              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11306                  "    Writing row of pixels (0)");
11307
11308           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11309
11310           if (p == (const Quantum *) NULL)
11311             break;
11312
11313           if (mng_info->IsPalette)
11314             {
11315               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11316                 quantum_info,GrayQuantum,ping_pixels,exception);
11317               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11318                   mng_info->write_png_depth &&
11319                   mng_info->write_png_depth != old_bit_depth)
11320                 {
11321                   /* Undo pixel scaling */
11322                   for (i=0; i < (ssize_t) image->columns; i++)
11323                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11324                      >> (8-old_bit_depth));
11325                 }
11326             }
11327
11328           else
11329             {
11330               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11331                 quantum_info,RedQuantum,ping_pixels,exception);
11332             }
11333
11334           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11335             for (i=0; i < (ssize_t) image->columns; i++)
11336                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11337                       255 : 0);
11338
11339           if (logging != MagickFalse && y == 0)
11340             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11341                 "    Writing row of pixels (1)");
11342
11343           png_write_row(ping,ping_pixels);
11344
11345           status=SetImageProgress(image,SaveImageTag,
11346               (MagickOffsetType) (pass * image->rows + y),
11347               num_passes * image->rows);
11348
11349           if (status == MagickFalse)
11350             break;
11351         }
11352       }
11353     }
11354
11355   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11356     {
11357       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11358           !mng_info->write_png48 && !mng_info->write_png64 &&
11359           !mng_info->write_png32) && (image_matte != MagickFalse ||
11360           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11361           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11362         {
11363           register const Quantum
11364             *p;
11365
11366           for (pass=0; pass < num_passes; pass++)
11367           {
11368
11369           for (y=0; y < (ssize_t) image->rows; y++)
11370           {
11371             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11372
11373             if (p == (const Quantum *) NULL)
11374               break;
11375
11376             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11377               {
11378                 if (mng_info->IsPalette)
11379                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11380                     quantum_info,GrayQuantum,ping_pixels,exception);
11381
11382                 else
11383                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11384                     quantum_info,RedQuantum,ping_pixels,exception);
11385
11386                 if (logging != MagickFalse && y == 0)
11387                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11388                        "    Writing GRAY PNG pixels (2)");
11389               }
11390
11391             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11392               {
11393                 if (logging != MagickFalse && y == 0)
11394                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11395                          "    Writing GRAY_ALPHA PNG pixels (2)");
11396
11397                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11398                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11399               }
11400
11401             if (logging != MagickFalse && y == 0)
11402               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11403                   "    Writing row of pixels (2)");
11404
11405             png_write_row(ping,ping_pixels);
11406
11407             status=SetImageProgress(image,SaveImageTag,
11408               (MagickOffsetType) (pass * image->rows + y),
11409               num_passes * image->rows);
11410
11411             if (status == MagickFalse)
11412               break;
11413             }
11414           }
11415         }
11416
11417       else
11418         {
11419           register const Quantum
11420             *p;
11421
11422           for (pass=0; pass < num_passes; pass++)
11423           {
11424             if ((image_depth > 8) ||
11425                 mng_info->write_png24 ||
11426                 mng_info->write_png32 ||
11427                 mng_info->write_png48 ||
11428                 mng_info->write_png64 ||
11429                 (!mng_info->write_png8 && !mng_info->IsPalette))
11430             {
11431               for (y=0; y < (ssize_t) image->rows; y++)
11432               {
11433                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11434
11435                 if (p == (const Quantum *) NULL)
11436                   break;
11437
11438                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11439                   {
11440                     if (image->storage_class == DirectClass)
11441                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11442                         quantum_info,RedQuantum,ping_pixels,exception);
11443
11444                     else
11445                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11446                         quantum_info,GrayQuantum,ping_pixels,exception);
11447                   }
11448
11449                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11450                   {
11451                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11452                       quantum_info,GrayAlphaQuantum,ping_pixels,
11453                       exception);
11454
11455                     if (logging != MagickFalse && y == 0)
11456                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11457                            "    Writing GRAY_ALPHA PNG pixels (3)");
11458                   }
11459
11460                 else if (image_matte != MagickFalse)
11461                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11462                     quantum_info,RGBAQuantum,ping_pixels,exception);
11463
11464                 else
11465                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11466                     quantum_info,RGBQuantum,ping_pixels,exception);
11467
11468                 if (logging != MagickFalse && y == 0)
11469                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11470                       "    Writing row of pixels (3)");
11471
11472                 png_write_row(ping,ping_pixels);
11473
11474                 status=SetImageProgress(image,SaveImageTag,
11475                   (MagickOffsetType) (pass * image->rows + y),
11476                   num_passes * image->rows);
11477
11478                 if (status == MagickFalse)
11479                   break;
11480               }
11481             }
11482
11483           else
11484             /* not ((image_depth > 8) ||
11485                 mng_info->write_png24 || mng_info->write_png32 ||
11486                 mng_info->write_png48 || mng_info->write_png64 ||
11487                 (!mng_info->write_png8 && !mng_info->IsPalette))
11488              */
11489             {
11490               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11491                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11492                 {
11493                   if (logging != MagickFalse)
11494                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11495                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11496
11497                   SetQuantumDepth(image,quantum_info,8);
11498                   image_depth=8;
11499                 }
11500
11501               for (y=0; y < (ssize_t) image->rows; y++)
11502               {
11503                 if (logging != MagickFalse && y == 0)
11504                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11505                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11506                     pass);
11507
11508                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11509
11510                 if (p == (const Quantum *) NULL)
11511                   break;
11512
11513                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11514                   {
11515                     SetQuantumDepth(image,quantum_info,image->depth);
11516
11517                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11518                        quantum_info,GrayQuantum,ping_pixels,exception);
11519                   }
11520
11521                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11522                   {
11523                     if (logging != MagickFalse && y == 0)
11524                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11525                            "  Writing GRAY_ALPHA PNG pixels (4)");
11526
11527                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11528                          quantum_info,GrayAlphaQuantum,ping_pixels,
11529                          exception);
11530                   }
11531
11532                 else
11533                   {
11534                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11535                       quantum_info,IndexQuantum,ping_pixels,exception);
11536
11537                     if (logging != MagickFalse && y <= 2)
11538                     {
11539                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11540                           "  Writing row of non-gray pixels (4)");
11541
11542                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11543                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11544                           (int)ping_pixels[0],(int)ping_pixels[1]);
11545                     }
11546                   }
11547                 png_write_row(ping,ping_pixels);
11548
11549                 status=SetImageProgress(image,SaveImageTag,
11550                   (MagickOffsetType) (pass * image->rows + y),
11551                   num_passes * image->rows);
11552
11553                 if (status == MagickFalse)
11554                   break;
11555               }
11556             }
11557           }
11558         }
11559     }
11560
11561   if (quantum_info != (QuantumInfo *) NULL)
11562     quantum_info=DestroyQuantumInfo(quantum_info);
11563
11564   if (logging != MagickFalse)
11565     {
11566       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11567         "  Wrote PNG image data");
11568
11569       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11570         "    Width: %.20g",(double) ping_width);
11571
11572       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11573         "    Height: %.20g",(double) ping_height);
11574
11575       if (mng_info->write_png_depth)
11576         {
11577           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11578             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11579         }
11580
11581       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11582         "    PNG bit-depth written: %d",ping_bit_depth);
11583
11584       if (mng_info->write_png_colortype)
11585         {
11586           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11587             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11588         }
11589
11590       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11591         "    PNG color-type written: %d",ping_color_type);
11592
11593       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11594         "    PNG Interlace method: %d",ping_interlace_method);
11595     }
11596   /*
11597     Generate text chunks after IDAT.
11598   */
11599   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11600   {
11601     ResetImagePropertyIterator(image);
11602     property=GetNextImageProperty(image);
11603     while (property != (const char *) NULL)
11604     {
11605       png_textp
11606         text;
11607
11608       value=GetImageProperty(image,property,exception);
11609
11610       /* Don't write any "png:" or "jpeg:" properties; those are just for
11611        * "identify" or for passing through to another JPEG
11612        */
11613       if ((LocaleNCompare(property,"png:",4) != 0 &&
11614            LocaleNCompare(property,"jpeg:",5) != 0) &&
11615
11616
11617           /* Suppress density and units if we wrote a pHYs chunk */
11618           (ping_exclude_pHYs != MagickFalse      ||
11619           LocaleCompare(property,"density") != 0 ||
11620           LocaleCompare(property,"units") != 0) &&
11621
11622           /* Suppress the IM-generated Date:create and Date:modify */
11623           (ping_exclude_date == MagickFalse      ||
11624           LocaleNCompare(property, "Date:",5) != 0))
11625         {
11626         if (value != (const char *) NULL)
11627           {
11628
11629 #if PNG_LIBPNG_VER >= 10400
11630             text=(png_textp) png_malloc(ping,
11631                  (png_alloc_size_t) sizeof(png_text));
11632 #else
11633             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11634 #endif
11635             text[0].key=(char *) property;
11636             text[0].text=(char *) value;
11637             text[0].text_length=strlen(value);
11638
11639             if (ping_exclude_tEXt != MagickFalse)
11640                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11641
11642             else if (ping_exclude_zTXt != MagickFalse)
11643                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11644
11645             else
11646             {
11647                text[0].compression=image_info->compression == NoCompression ||
11648                  (image_info->compression == UndefinedCompression &&
11649                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11650                  PNG_TEXT_COMPRESSION_zTXt ;
11651             }
11652
11653             if (logging != MagickFalse)
11654               {
11655                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11656                   "  Setting up text chunk");
11657
11658                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11659                   "    keyword: '%s'",text[0].key);
11660               }
11661
11662             png_set_text(ping,ping_info,text,1);
11663             png_free(ping,text);
11664           }
11665         }
11666       property=GetNextImageProperty(image);
11667     }
11668   }
11669
11670   /* write eXIf profile */
11671   if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11672     {
11673       char
11674         *name;
11675
11676       ResetImageProfileIterator(image);
11677
11678       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
11679       {
11680         if (LocaleCompare(name,"exif") == 0)
11681           {
11682             const StringInfo
11683               *profile;
11684
11685             profile=GetImageProfile(image,name);
11686
11687             if (profile != (StringInfo *) NULL)
11688               {
11689                 png_uint_32
11690                   length;
11691
11692                 unsigned char
11693                   chunk[4],
11694                   *data;
11695
11696                StringInfo
11697                  *ping_profile;
11698
11699                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11700                   "  Have eXIf profile");
11701
11702                ping_profile=CloneStringInfo(profile);
11703                data=GetStringInfoDatum(ping_profile),
11704                length=(png_uint_32) GetStringInfoLength(ping_profile);
11705
11706                PNGType(chunk,mng_eXIf);
11707                if (length < 7)
11708                  {
11709                    ping_profile=DestroyStringInfo(ping_profile);
11710                    break;  /* otherwise crashes */
11711                  }
11712
11713                /* skip the "Exif\0\0" JFIF Exif Header ID */
11714                length -= 6;
11715
11716                LogPNGChunk(logging,chunk,length);
11717                (void) WriteBlobMSBULong(image,length);
11718                (void) WriteBlob(image,4,chunk);
11719                (void) WriteBlob(image,length,data+6);
11720                (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11721                  data+6, (uInt) length));
11722                ping_profile=DestroyStringInfo(ping_profile);
11723                break;
11724              }
11725          }
11726        name=GetNextImageProfile(image);
11727      }
11728   }
11729
11730   if (logging != MagickFalse)
11731     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11732       "  Writing PNG end info");
11733
11734   png_write_end(ping,ping_info);
11735
11736   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11737     {
11738       if (mng_info->page.x || mng_info->page.y ||
11739           (ping_width != mng_info->page.width) ||
11740           (ping_height != mng_info->page.height))
11741         {
11742           unsigned char
11743             chunk[32];
11744
11745           /*
11746             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11747           */
11748           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11749           PNGType(chunk,mng_FRAM);
11750           LogPNGChunk(logging,mng_FRAM,27L);
11751           chunk[4]=4;
11752           chunk[5]=0;  /* frame name separator (no name) */
11753           chunk[6]=1;  /* flag for changing delay, for next frame only */
11754           chunk[7]=0;  /* flag for changing frame timeout */
11755           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11756           chunk[9]=0;  /* flag for changing frame sync_id */
11757           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11758           chunk[14]=0; /* clipping boundaries delta type */
11759           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11760           PNGLong(chunk+19,
11761              (png_uint_32) (mng_info->page.x + ping_width));
11762           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11763           PNGLong(chunk+27,
11764              (png_uint_32) (mng_info->page.y + ping_height));
11765           (void) WriteBlob(image,31,chunk);
11766           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11767           mng_info->old_framing_mode=4;
11768           mng_info->framing_mode=1;
11769         }
11770
11771       else
11772         mng_info->framing_mode=3;
11773     }
11774   if (mng_info->write_mng && !mng_info->need_fram &&
11775       ((int) image->dispose == 3))
11776      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11777
11778   /*
11779     Free PNG resources.
11780   */
11781
11782   png_destroy_write_struct(&ping,&ping_info);
11783
11784   pixel_info=RelinquishVirtualMemory(pixel_info);
11785
11786   if (ping_have_blob != MagickFalse)
11787      (void) CloseBlob(image);
11788
11789   image_info=DestroyImageInfo(image_info);
11790   image=DestroyImage(image);
11791
11792   /* Store bit depth actually written */
11793   s[0]=(char) ping_bit_depth;
11794   s[1]='\0';
11795
11796   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11797
11798   if (logging != MagickFalse)
11799     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11800       "  exit WriteOnePNGImage()");
11801
11802 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11803   UnlockSemaphoreInfo(ping_semaphore);
11804 #endif
11805
11806    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11807     *    Throwing an Exception when an error occurs.
11808     */
11809
11810   return(MagickTrue);
11811 /*  End write one PNG image */
11812
11813 }
11814
11815 /*
11816 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11817 %                                                                             %
11818 %                                                                             %
11819 %                                                                             %
11820 %   W r i t e P N G I m a g e                                                 %
11821 %                                                                             %
11822 %                                                                             %
11823 %                                                                             %
11824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11825 %
11826 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11827 %  Multiple-image Network Graphics (MNG) image file.
11828 %
11829 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11830 %
11831 %  The format of the WritePNGImage method is:
11832 %
11833 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11834 %        Image *image,ExceptionInfo *exception)
11835 %
11836 %  A description of each parameter follows:
11837 %
11838 %    o image_info: the image info.
11839 %
11840 %    o image:  The image.
11841 %
11842 %    o exception: return any errors or warnings in this structure.
11843 %
11844 %  Returns MagickTrue on success, MagickFalse on failure.
11845 %
11846 %  Communicating with the PNG encoder:
11847 %
11848 %  While the datastream written is always in PNG format and normally would
11849 %  be given the "png" file extension, this method also writes the following
11850 %  pseudo-formats which are subsets of png:
11851 %
11852 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11853 %               a depth greater than 8, the depth is reduced. If transparency
11854 %               is present, the tRNS chunk must only have values 0 and 255
11855 %               (i.e., transparency is binary: fully opaque or fully
11856 %               transparent).  If other values are present they will be
11857 %               50%-thresholded to binary transparency.  If more than 256
11858 %               colors are present, they will be quantized to the 4-4-4-1,
11859 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11860 %               of any resulting fully-transparent pixels is changed to
11861 %               the image's background color.
11862 %
11863 %               If you want better quantization or dithering of the colors
11864 %               or alpha than that, you need to do it before calling the
11865 %               PNG encoder. The pixels contain 8-bit indices even if
11866 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11867 %               images will be written as indexed PNG files even though the
11868 %               PNG grayscale type might be slightly more efficient.  Please
11869 %               note that writing to the PNG8 format may result in loss
11870 %               of color and alpha data.
11871 %
11872 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11873 %               chunk can be present to convey binary transparency by naming
11874 %               one of the colors as transparent.  The only loss incurred
11875 %               is reduction of sample depth to 8.  If the image has more
11876 %               than one transparent color, has semitransparent pixels, or
11877 %               has an opaque pixel with the same RGB components as the
11878 %               transparent color, an image is not written.
11879 %
11880 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11881 %               transparency is permitted, i.e., the alpha sample for
11882 %               each pixel can have any value from 0 to 255. The alpha
11883 %               channel is present even if the image is fully opaque.
11884 %               The only loss in data is the reduction of the sample depth
11885 %               to 8.
11886 %
11887 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11888 %               chunk can be present to convey binary transparency by naming
11889 %               one of the colors as transparent.  If the image has more
11890 %               than one transparent color, has semitransparent pixels, or
11891 %               has an opaque pixel with the same RGB components as the
11892 %               transparent color, an image is not written.
11893 %
11894 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11895 %               transparency is permitted, i.e., the alpha sample for
11896 %               each pixel can have any value from 0 to 65535. The alpha
11897 %               channel is present even if the image is fully opaque.
11898 %
11899 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11900 %               image, if the input was a PNG, is written.  If these values
11901 %               cannot be found, or if the pixels have been changed in a way
11902 %               that makes this impossible, then "PNG00" falls back to the
11903 %               regular "PNG" format.
11904 %
11905 %    o -define: For more precise control of the PNG output, you can use the
11906 %               Image options "png:bit-depth" and "png:color-type".  These
11907 %               can be set from the commandline with "-define" and also
11908 %               from the application programming interfaces.  The options
11909 %               are case-independent and are converted to lowercase before
11910 %               being passed to this encoder.
11911 %
11912 %               png:color-type can be 0, 2, 3, 4, or 6.
11913 %
11914 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11915 %               be 1, 2, 4, 8, or 16.
11916 %
11917 %               When png:color-type is 2 (RGB), png:bit-depth can
11918 %               be 8 or 16.
11919 %
11920 %               When png:color-type is 3 (Indexed), png:bit-depth can
11921 %               be 1, 2, 4, or 8.  This refers to the number of bits
11922 %               used to store the index.  The color samples always have
11923 %               bit-depth 8 in indexed PNG files.
11924 %
11925 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11926 %               png:bit-depth can be 8 or 16.
11927 %
11928 %               If the image cannot be written without loss with the
11929 %               requested bit-depth and color-type, a PNG file will not
11930 %               be written, a warning will be issued, and the encoder will
11931 %               return MagickFalse.
11932 %
11933 %  Since image encoders should not be responsible for the "heavy lifting",
11934 %  the user should make sure that ImageMagick has already reduced the
11935 %  image depth and number of colors and limit transparency to binary
11936 %  transparency prior to attempting to write the image with depth, color,
11937 %  or transparency limitations.
11938 %
11939 %  Note that another definition, "png:bit-depth-written" exists, but it
11940 %  is not intended for external use.  It is only used internally by the
11941 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11942 %
11943 %  As of version 6.6.6 the following optimizations are always done:
11944 %
11945 %   o  32-bit depth is reduced to 16.
11946 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11947 %      high byte and low byte are identical.
11948 %   o  Palette is sorted to remove unused entries and to put a
11949 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11950 %   o  Opaque matte channel is removed when writing an indexed PNG.
11951 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11952 %      this can be done without loss and a larger bit depth N was not
11953 %      requested via the "-define png:bit-depth=N" option.
11954 %   o  If matte channel is present but only one transparent color is
11955 %      present, RGB+tRNS is written instead of RGBA
11956 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11957 %      was requested when converting an opaque image).
11958 %
11959 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11960 */
11961 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11962   Image *image,ExceptionInfo *exception)
11963 {
11964   MagickBooleanType
11965     excluding,
11966     logging,
11967     status;
11968
11969   MngInfo
11970     *mng_info;
11971
11972   const char
11973     *value;
11974
11975   int
11976     source;
11977
11978   /*
11979     Open image file.
11980   */
11981   assert(image_info != (const ImageInfo *) NULL);
11982   assert(image_info->signature == MagickCoreSignature);
11983   assert(image != (Image *) NULL);
11984   assert(image->signature == MagickCoreSignature);
11985   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11986   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11987   /*
11988     Allocate a MngInfo structure.
11989   */
11990   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11991
11992   if (mng_info == (MngInfo *) NULL)
11993     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11994
11995   /*
11996     Initialize members of the MngInfo structure.
11997   */
11998   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11999   mng_info->image=image;
12000   mng_info->equal_backgrounds=MagickTrue;
12001
12002   /* See if user has requested a specific PNG subformat */
12003
12004   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12005   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12006   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12007   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
12008   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
12009
12010   value=GetImageOption(image_info,"png:format");
12011
12012   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
12013     {
12014       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12015          "  Format=%s",value);
12016
12017       mng_info->write_png8 = MagickFalse;
12018       mng_info->write_png24 = MagickFalse;
12019       mng_info->write_png32 = MagickFalse;
12020       mng_info->write_png48 = MagickFalse;
12021       mng_info->write_png64 = MagickFalse;
12022
12023       if (LocaleCompare(value,"png8") == 0)
12024         mng_info->write_png8 = MagickTrue;
12025
12026       else if (LocaleCompare(value,"png24") == 0)
12027         mng_info->write_png24 = MagickTrue;
12028
12029       else if (LocaleCompare(value,"png32") == 0)
12030         mng_info->write_png32 = MagickTrue;
12031
12032       else if (LocaleCompare(value,"png48") == 0)
12033         mng_info->write_png48 = MagickTrue;
12034
12035       else if (LocaleCompare(value,"png64") == 0)
12036         mng_info->write_png64 = MagickTrue;
12037
12038       else if ((LocaleCompare(value,"png00") == 0) ||
12039          LocaleCompare(image_info->magick,"PNG00") == 0)
12040         {
12041           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
12042           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
12043
12044           if (value != (char *) NULL)
12045             {
12046               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12047                  "  png00 inherited bit depth=%s",value);
12048
12049               if (LocaleCompare(value,"1") == 0)
12050                 mng_info->write_png_depth = 1;
12051
12052               else if (LocaleCompare(value,"2") == 0)
12053                 mng_info->write_png_depth = 2;
12054
12055               else if (LocaleCompare(value,"4") == 0)
12056                 mng_info->write_png_depth = 4;
12057
12058               else if (LocaleCompare(value,"8") == 0)
12059                 mng_info->write_png_depth = 8;
12060
12061               else if (LocaleCompare(value,"16") == 0)
12062                 mng_info->write_png_depth = 16;
12063             }
12064
12065           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
12066
12067           if (value != (char *) NULL)
12068             {
12069               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12070                  "  png00 inherited color type=%s",value);
12071
12072               if (LocaleCompare(value,"0") == 0)
12073                 mng_info->write_png_colortype = 1;
12074
12075               else if (LocaleCompare(value,"2") == 0)
12076                 mng_info->write_png_colortype = 3;
12077
12078               else if (LocaleCompare(value,"3") == 0)
12079                 mng_info->write_png_colortype = 4;
12080
12081               else if (LocaleCompare(value,"4") == 0)
12082                 mng_info->write_png_colortype = 5;
12083
12084               else if (LocaleCompare(value,"6") == 0)
12085                 mng_info->write_png_colortype = 7;
12086             }
12087         }
12088     }
12089
12090   if (mng_info->write_png8)
12091     {
12092       mng_info->write_png_colortype = /* 3 */ 4;
12093       mng_info->write_png_depth = 8;
12094       image->depth = 8;
12095     }
12096
12097   if (mng_info->write_png24)
12098     {
12099       mng_info->write_png_colortype = /* 2 */ 3;
12100       mng_info->write_png_depth = 8;
12101       image->depth = 8;
12102
12103       if (image->alpha_trait != UndefinedPixelTrait)
12104         (void) SetImageType(image,TrueColorAlphaType,exception);
12105
12106       else
12107         (void) SetImageType(image,TrueColorType,exception);
12108
12109       (void) SyncImage(image,exception);
12110     }
12111
12112   if (mng_info->write_png32)
12113     {
12114       mng_info->write_png_colortype = /* 6 */  7;
12115       mng_info->write_png_depth = 8;
12116       image->depth = 8;
12117       image->alpha_trait = BlendPixelTrait;
12118
12119       (void) SetImageType(image,TrueColorAlphaType,exception);
12120       (void) SyncImage(image,exception);
12121     }
12122
12123   if (mng_info->write_png48)
12124     {
12125       mng_info->write_png_colortype = /* 2 */ 3;
12126       mng_info->write_png_depth = 16;
12127       image->depth = 16;
12128
12129       if (image->alpha_trait != UndefinedPixelTrait)
12130         (void) SetImageType(image,TrueColorAlphaType,exception);
12131
12132       else
12133         (void) SetImageType(image,TrueColorType,exception);
12134
12135       (void) SyncImage(image,exception);
12136     }
12137
12138   if (mng_info->write_png64)
12139     {
12140       mng_info->write_png_colortype = /* 6 */  7;
12141       mng_info->write_png_depth = 16;
12142       image->depth = 16;
12143       image->alpha_trait = BlendPixelTrait;
12144
12145       (void) SetImageType(image,TrueColorAlphaType,exception);
12146       (void) SyncImage(image,exception);
12147     }
12148
12149   value=GetImageOption(image_info,"png:bit-depth");
12150
12151   if (value != (char *) NULL)
12152     {
12153       if (LocaleCompare(value,"1") == 0)
12154         mng_info->write_png_depth = 1;
12155
12156       else if (LocaleCompare(value,"2") == 0)
12157         mng_info->write_png_depth = 2;
12158
12159       else if (LocaleCompare(value,"4") == 0)
12160         mng_info->write_png_depth = 4;
12161
12162       else if (LocaleCompare(value,"8") == 0)
12163         mng_info->write_png_depth = 8;
12164
12165       else if (LocaleCompare(value,"16") == 0)
12166         mng_info->write_png_depth = 16;
12167
12168       else
12169         (void) ThrowMagickException(exception,
12170              GetMagickModule(),CoderWarning,
12171              "ignoring invalid defined png:bit-depth",
12172              "=%s",value);
12173
12174       if (logging != MagickFalse)
12175         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12176           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12177     }
12178
12179   value=GetImageOption(image_info,"png:color-type");
12180
12181   if (value != (char *) NULL)
12182     {
12183       /* We must store colortype+1 because 0 is a valid colortype */
12184       if (LocaleCompare(value,"0") == 0)
12185         mng_info->write_png_colortype = 1;
12186
12187       else if (LocaleCompare(value,"1") == 0)
12188         mng_info->write_png_colortype = 2;
12189
12190       else if (LocaleCompare(value,"2") == 0)
12191         mng_info->write_png_colortype = 3;
12192
12193       else if (LocaleCompare(value,"3") == 0)
12194         mng_info->write_png_colortype = 4;
12195
12196       else if (LocaleCompare(value,"4") == 0)
12197         mng_info->write_png_colortype = 5;
12198
12199       else if (LocaleCompare(value,"6") == 0)
12200         mng_info->write_png_colortype = 7;
12201
12202       else
12203         (void) ThrowMagickException(exception,
12204              GetMagickModule(),CoderWarning,
12205              "ignoring invalid defined png:color-type",
12206              "=%s",value);
12207
12208       if (logging != MagickFalse)
12209         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12210           "  png:color-type=%d was defined.\n",
12211           mng_info->write_png_colortype-1);
12212     }
12213
12214   /* Check for chunks to be excluded:
12215    *
12216    * The default is to not exclude any known chunks except for any
12217    * listed in the "unused_chunks" array, above.
12218    *
12219    * Chunks can be listed for exclusion via a "png:exclude-chunk"
12220    * define (in the image properties or in the image artifacts)
12221    * or via a mng_info member.  For convenience, in addition
12222    * to or instead of a comma-separated list of chunks, the
12223    * "exclude-chunk" string can be simply "all" or "none".
12224    *
12225    * Note that the "-strip" option provides a convenient way of
12226    * doing the equivalent of
12227    *
12228    *    -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12229    *            iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12230    *
12231    * The exclude-chunk define takes priority over the mng_info.
12232    *
12233    * A "png:include-chunk" define takes  priority over both the
12234    * mng_info and the "png:exclude-chunk" define.  Like the
12235    * "exclude-chunk" string, it can define "all" or "none" as
12236    * well as a comma-separated list.  Chunks that are unknown to
12237    * ImageMagick are always excluded, regardless of their "copy-safe"
12238    * status according to the PNG specification, and even if they
12239    * appear in the "include-chunk" list. Such defines appearing among
12240    * the image options take priority over those found among the image
12241    * artifacts.
12242    *
12243    * Finally, all chunks listed in the "unused_chunks" array are
12244    * automatically excluded, regardless of the other instructions
12245    * or lack thereof.
12246    *
12247    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12248    * will not be written and the gAMA chunk will only be written if it
12249    * is not between .45 and .46, or approximately (1.0/2.2).
12250    *
12251    * If you exclude tRNS and the image has transparency, the colortype
12252    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12253    *
12254    * The -strip option causes StripImage() to set the png:include-chunk
12255    * artifact to "none,trns,gama".
12256    */
12257
12258   mng_info->ping_exclude_bKGD=MagickFalse;
12259   mng_info->ping_exclude_caNv=MagickFalse;
12260   mng_info->ping_exclude_cHRM=MagickFalse;
12261   mng_info->ping_exclude_date=MagickFalse;
12262   mng_info->ping_exclude_eXIf=MagickFalse;
12263   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12264   mng_info->ping_exclude_gAMA=MagickFalse;
12265   mng_info->ping_exclude_iCCP=MagickFalse;
12266   /* mng_info->ping_exclude_iTXt=MagickFalse; */
12267   mng_info->ping_exclude_oFFs=MagickFalse;
12268   mng_info->ping_exclude_pHYs=MagickFalse;
12269   mng_info->ping_exclude_sRGB=MagickFalse;
12270   mng_info->ping_exclude_tEXt=MagickFalse;
12271   mng_info->ping_exclude_tIME=MagickFalse;
12272   mng_info->ping_exclude_tRNS=MagickFalse;
12273   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12274   mng_info->ping_exclude_zTXt=MagickFalse;
12275
12276   mng_info->ping_preserve_colormap=MagickFalse;
12277
12278   value=GetImageOption(image_info,"png:preserve-colormap");
12279   if (value == NULL)
12280      value=GetImageArtifact(image,"png:preserve-colormap");
12281   if (value != NULL)
12282      mng_info->ping_preserve_colormap=MagickTrue;
12283
12284   mng_info->ping_preserve_iCCP=MagickFalse;
12285
12286   value=GetImageOption(image_info,"png:preserve-iCCP");
12287   if (value == NULL)
12288      value=GetImageArtifact(image,"png:preserve-iCCP");
12289   if (value != NULL)
12290      mng_info->ping_preserve_iCCP=MagickTrue;
12291
12292   /* These compression-level, compression-strategy, and compression-filter
12293    * defines take precedence over values from the -quality option.
12294    */
12295   value=GetImageOption(image_info,"png:compression-level");
12296   if (value == NULL)
12297      value=GetImageArtifact(image,"png:compression-level");
12298   if (value != NULL)
12299   {
12300       /* We have to add 1 to everything because 0 is a valid input,
12301        * and we want to use 0 (the default) to mean undefined.
12302        */
12303       if (LocaleCompare(value,"0") == 0)
12304         mng_info->write_png_compression_level = 1;
12305
12306       else if (LocaleCompare(value,"1") == 0)
12307         mng_info->write_png_compression_level = 2;
12308
12309       else if (LocaleCompare(value,"2") == 0)
12310         mng_info->write_png_compression_level = 3;
12311
12312       else if (LocaleCompare(value,"3") == 0)
12313         mng_info->write_png_compression_level = 4;
12314
12315       else if (LocaleCompare(value,"4") == 0)
12316         mng_info->write_png_compression_level = 5;
12317
12318       else if (LocaleCompare(value,"5") == 0)
12319         mng_info->write_png_compression_level = 6;
12320
12321       else if (LocaleCompare(value,"6") == 0)
12322         mng_info->write_png_compression_level = 7;
12323
12324       else if (LocaleCompare(value,"7") == 0)
12325         mng_info->write_png_compression_level = 8;
12326
12327       else if (LocaleCompare(value,"8") == 0)
12328         mng_info->write_png_compression_level = 9;
12329
12330       else if (LocaleCompare(value,"9") == 0)
12331         mng_info->write_png_compression_level = 10;
12332
12333       else
12334         (void) ThrowMagickException(exception,
12335              GetMagickModule(),CoderWarning,
12336              "ignoring invalid defined png:compression-level",
12337              "=%s",value);
12338     }
12339
12340   value=GetImageOption(image_info,"png:compression-strategy");
12341   if (value == NULL)
12342      value=GetImageArtifact(image,"png:compression-strategy");
12343   if (value != NULL)
12344   {
12345       if (LocaleCompare(value,"0") == 0)
12346         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12347
12348       else if (LocaleCompare(value,"1") == 0)
12349         mng_info->write_png_compression_strategy = Z_FILTERED+1;
12350
12351       else if (LocaleCompare(value,"2") == 0)
12352         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12353
12354       else if (LocaleCompare(value,"3") == 0)
12355 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
12356         mng_info->write_png_compression_strategy = Z_RLE+1;
12357 #else
12358         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12359 #endif
12360
12361       else if (LocaleCompare(value,"4") == 0)
12362 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
12363         mng_info->write_png_compression_strategy = Z_FIXED+1;
12364 #else
12365         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12366 #endif
12367
12368       else
12369         (void) ThrowMagickException(exception,
12370              GetMagickModule(),CoderWarning,
12371              "ignoring invalid defined png:compression-strategy",
12372              "=%s",value);
12373     }
12374
12375   value=GetImageOption(image_info,"png:compression-filter");
12376   if (value == NULL)
12377      value=GetImageArtifact(image,"png:compression-filter");
12378   if (value != NULL)
12379   {
12380       /* To do: combinations of filters allowed by libpng
12381        * masks 0x08 through 0xf8
12382        *
12383        * Implement this as a comma-separated list of 0,1,2,3,4,5
12384        * where 5 is a special case meaning PNG_ALL_FILTERS.
12385        */
12386
12387       if (LocaleCompare(value,"0") == 0)
12388         mng_info->write_png_compression_filter = 1;
12389
12390       else if (LocaleCompare(value,"1") == 0)
12391         mng_info->write_png_compression_filter = 2;
12392
12393       else if (LocaleCompare(value,"2") == 0)
12394         mng_info->write_png_compression_filter = 3;
12395
12396       else if (LocaleCompare(value,"3") == 0)
12397         mng_info->write_png_compression_filter = 4;
12398
12399       else if (LocaleCompare(value,"4") == 0)
12400         mng_info->write_png_compression_filter = 5;
12401
12402       else if (LocaleCompare(value,"5") == 0)
12403         mng_info->write_png_compression_filter = 6;
12404
12405       else
12406         (void) ThrowMagickException(exception,
12407              GetMagickModule(),CoderWarning,
12408              "ignoring invalid defined png:compression-filter",
12409              "=%s",value);
12410   }
12411
12412   for (source=0; source<8; source++)
12413   {
12414     value = NULL;
12415
12416     if (source == 0)
12417       value=GetImageOption(image_info,"png:exclude-chunks");
12418
12419     if (source == 1)
12420       value=GetImageArtifact(image,"png:exclude-chunks");
12421
12422     if (source == 2)
12423       value=GetImageOption(image_info,"png:exclude-chunk");
12424
12425     if (source == 3)
12426       value=GetImageArtifact(image,"png:exclude-chunk");
12427
12428     if (source == 4)
12429       value=GetImageOption(image_info,"png:include-chunks");
12430
12431     if (source == 5)
12432       value=GetImageArtifact(image,"png:include-chunks");
12433
12434     if (source == 6)
12435       value=GetImageOption(image_info,"png:include-chunk");
12436
12437     if (source == 7)
12438       value=GetImageArtifact(image,"png:include-chunk");
12439
12440     if (value == NULL)
12441        continue;
12442
12443     if (source < 4)
12444       excluding = MagickTrue;
12445     else
12446       excluding = MagickFalse;
12447
12448     if (logging != MagickFalse)
12449       {
12450         if (source == 0 || source == 2)
12451            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12452               "  png:exclude-chunk=%s found in image options.\n", value);
12453         else if (source == 1 || source == 3)
12454            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12455               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12456         else if (source == 4 || source == 6)
12457            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12458               "  png:include-chunk=%s found in image options.\n", value);
12459         else /* if (source == 5 || source == 7) */
12460            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12461               "  png:include-chunk=%s found in image artifacts.\n", value);
12462       }
12463
12464     if (IsOptionMember("all",value) != MagickFalse)
12465       {
12466         mng_info->ping_exclude_bKGD=excluding;
12467         mng_info->ping_exclude_caNv=excluding;
12468         mng_info->ping_exclude_cHRM=excluding;
12469         mng_info->ping_exclude_date=excluding;
12470         mng_info->ping_exclude_EXIF=excluding;
12471         mng_info->ping_exclude_eXIf=excluding;
12472         mng_info->ping_exclude_gAMA=excluding;
12473         mng_info->ping_exclude_iCCP=excluding;
12474         /* mng_info->ping_exclude_iTXt=excluding; */
12475         mng_info->ping_exclude_oFFs=excluding;
12476         mng_info->ping_exclude_pHYs=excluding;
12477         mng_info->ping_exclude_sRGB=excluding;
12478         mng_info->ping_exclude_tEXt=excluding;
12479         mng_info->ping_exclude_tIME=excluding;
12480         mng_info->ping_exclude_tRNS=excluding;
12481         mng_info->ping_exclude_zCCP=excluding;
12482         mng_info->ping_exclude_zTXt=excluding;
12483       }
12484
12485     if (IsOptionMember("none",value) != MagickFalse)
12486       {
12487         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12488           MagickTrue;
12489         mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12490           MagickTrue;
12491         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12492           MagickTrue;
12493         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12494           MagickTrue;
12495         mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12496           MagickTrue;
12497         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12498           MagickTrue;
12499         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12500           MagickTrue;
12501         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12502           MagickTrue;
12503         /* mng_info->ping_exclude_iTXt=!excluding; */
12504         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12505           MagickTrue;
12506         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12507           MagickTrue;
12508         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12509           MagickTrue;
12510         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12511           MagickTrue;
12512         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12513           MagickTrue;
12514         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12515           MagickTrue;
12516         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12517           MagickTrue;
12518         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12519           MagickTrue;
12520       }
12521
12522     if (IsOptionMember("bkgd",value) != MagickFalse)
12523       mng_info->ping_exclude_bKGD=excluding;
12524
12525     if (IsOptionMember("caNv",value) != MagickFalse)
12526       mng_info->ping_exclude_caNv=excluding;
12527
12528     if (IsOptionMember("chrm",value) != MagickFalse)
12529       mng_info->ping_exclude_cHRM=excluding;
12530
12531     if (IsOptionMember("date",value) != MagickFalse)
12532       mng_info->ping_exclude_date=excluding;
12533
12534     if (IsOptionMember("exif",value) != MagickFalse)
12535       {
12536         mng_info->ping_exclude_EXIF=excluding;
12537         mng_info->ping_exclude_eXIf=excluding;
12538       }
12539
12540     if (IsOptionMember("gama",value) != MagickFalse)
12541       mng_info->ping_exclude_gAMA=excluding;
12542
12543     if (IsOptionMember("iccp",value) != MagickFalse)
12544       mng_info->ping_exclude_iCCP=excluding;
12545
12546 #if 0
12547     if (IsOptionMember("itxt",value) != MagickFalse)
12548       mng_info->ping_exclude_iTXt=excluding;
12549 #endif
12550
12551     if (IsOptionMember("offs",value) != MagickFalse)
12552       mng_info->ping_exclude_oFFs=excluding;
12553
12554     if (IsOptionMember("phys",value) != MagickFalse)
12555       mng_info->ping_exclude_pHYs=excluding;
12556
12557     if (IsOptionMember("srgb",value) != MagickFalse)
12558       mng_info->ping_exclude_sRGB=excluding;
12559
12560     if (IsOptionMember("text",value) != MagickFalse)
12561       mng_info->ping_exclude_tEXt=excluding;
12562
12563     if (IsOptionMember("time",value) != MagickFalse)
12564       mng_info->ping_exclude_tIME=excluding;
12565
12566     if (IsOptionMember("trns",value) != MagickFalse)
12567       mng_info->ping_exclude_tRNS=excluding;
12568
12569     if (IsOptionMember("zccp",value) != MagickFalse)
12570       mng_info->ping_exclude_zCCP=excluding;
12571
12572     if (IsOptionMember("ztxt",value) != MagickFalse)
12573       mng_info->ping_exclude_zTXt=excluding;
12574   }
12575
12576   if (logging != MagickFalse)
12577   {
12578     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12579       "  Chunks to be excluded from the output png:");
12580     if (mng_info->ping_exclude_bKGD != MagickFalse)
12581       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12582           "    bKGD");
12583     if (mng_info->ping_exclude_caNv != MagickFalse)
12584       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12585           "    caNv");
12586     if (mng_info->ping_exclude_cHRM != MagickFalse)
12587       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12588           "    cHRM");
12589     if (mng_info->ping_exclude_date != MagickFalse)
12590       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12591           "    date");
12592     if (mng_info->ping_exclude_EXIF != MagickFalse)
12593       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12594           "    EXIF");
12595     if (mng_info->ping_exclude_eXIf != MagickFalse)
12596       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12597           "    eXIf");
12598     if (mng_info->ping_exclude_gAMA != MagickFalse)
12599       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12600           "    gAMA");
12601     if (mng_info->ping_exclude_iCCP != MagickFalse)
12602       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12603           "    iCCP");
12604 #if 0
12605     if (mng_info->ping_exclude_iTXt != MagickFalse)
12606       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12607           "    iTXt");
12608 #endif
12609
12610     if (mng_info->ping_exclude_oFFs != MagickFalse)
12611       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12612           "    oFFs");
12613     if (mng_info->ping_exclude_pHYs != MagickFalse)
12614       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12615           "    pHYs");
12616     if (mng_info->ping_exclude_sRGB != MagickFalse)
12617       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12618           "    sRGB");
12619     if (mng_info->ping_exclude_tEXt != MagickFalse)
12620       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12621           "    tEXt");
12622     if (mng_info->ping_exclude_tIME != MagickFalse)
12623       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12624           "    tIME");
12625     if (mng_info->ping_exclude_tRNS != MagickFalse)
12626       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12627           "    tRNS");
12628     if (mng_info->ping_exclude_zCCP != MagickFalse)
12629       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12630           "    zCCP");
12631     if (mng_info->ping_exclude_zTXt != MagickFalse)
12632       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12633           "    zTXt");
12634   }
12635
12636   mng_info->need_blob = MagickTrue;
12637
12638   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12639
12640   mng_info=MngInfoFreeStruct(mng_info);
12641
12642   if (logging != MagickFalse)
12643     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12644
12645   return(status);
12646 }
12647
12648 #if defined(JNG_SUPPORTED)
12649
12650 /* Write one JNG image */
12651 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12652    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12653 {
12654   Image
12655     *jpeg_image;
12656
12657   ImageInfo
12658     *jpeg_image_info;
12659
12660   MagickBooleanType
12661     logging,
12662     status;
12663
12664   size_t
12665     length;
12666
12667   unsigned char
12668     *blob,
12669     chunk[80],
12670     *p;
12671
12672   unsigned int
12673     jng_alpha_compression_method,
12674     jng_alpha_sample_depth,
12675     jng_color_type,
12676     transparent;
12677
12678   size_t
12679     jng_alpha_quality,
12680     jng_quality;
12681
12682   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12683     "  Enter WriteOneJNGImage()");
12684
12685   blob=(unsigned char *) NULL;
12686   jpeg_image=(Image *) NULL;
12687   jpeg_image_info=(ImageInfo *) NULL;
12688   length=0;
12689
12690   status=MagickTrue;
12691   transparent=image_info->type==GrayscaleAlphaType ||
12692      image_info->type==TrueColorAlphaType ||
12693      image->alpha_trait != UndefinedPixelTrait;
12694
12695   jng_alpha_sample_depth = 0;
12696
12697   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12698
12699   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12700
12701   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12702       image_info->quality;
12703
12704   if (jng_alpha_quality >= 1000)
12705     jng_alpha_quality /= 1000;
12706
12707   length=0;
12708
12709   if (transparent != 0)
12710     {
12711       jng_color_type=14;
12712
12713       /* Create JPEG blob, image, and image_info */
12714       if (logging != MagickFalse)
12715         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716           "  Creating jpeg_image_info for alpha.");
12717
12718       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12719
12720       if (jpeg_image_info == (ImageInfo *) NULL)
12721         {
12722           jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12723           ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12724         }
12725
12726       if (logging != MagickFalse)
12727         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12728           "  Creating jpeg_image.");
12729
12730       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12731       if (jpeg_image == (Image *) NULL)
12732         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12733       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12734       jpeg_image->alpha_trait=UndefinedPixelTrait;
12735       jpeg_image->quality=jng_alpha_quality;
12736       jpeg_image_info->type=GrayscaleType;
12737       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12738       (void) AcquireUniqueFilename(jpeg_image->filename);
12739       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12740         "%s",jpeg_image->filename);
12741     }
12742   else
12743     {
12744       jng_alpha_compression_method=0;
12745       jng_color_type=10;
12746       jng_alpha_sample_depth=0;
12747     }
12748
12749   /* To do: check bit depth of PNG alpha channel */
12750
12751   /* Check if image is grayscale. */
12752   if (image_info->type != TrueColorAlphaType && image_info->type !=
12753     TrueColorType && SetImageGray(image,exception))
12754     jng_color_type-=2;
12755
12756   if (logging != MagickFalse)
12757     {
12758         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12759           "    JNG Quality           = %d",(int) jng_quality);
12760         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12761           "    JNG Color Type        = %d",jng_color_type);
12762         if (transparent != 0)
12763           {
12764             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12765               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12766             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12767               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12768             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12769               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12770           }
12771     }
12772
12773   if (transparent != 0)
12774     {
12775       if (jng_alpha_compression_method==0)
12776         {
12777           const char
12778             *value;
12779
12780           /* Encode alpha as a grayscale PNG blob */
12781           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12782             exception);
12783           if (status == MagickFalse)
12784             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12785
12786           if (logging != MagickFalse)
12787             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12788               "  Creating PNG blob.");
12789
12790           (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12791              MagickPathExtent);
12792           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12793           jpeg_image_info->interlace=NoInterlace;
12794
12795           /* Exclude all ancillary chunks */
12796           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12797
12798           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12799             &length,exception);
12800
12801           /* Retrieve sample depth used */
12802           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12803           if (value != (char *) NULL)
12804             jng_alpha_sample_depth= (unsigned int) value[0];
12805         }
12806       else
12807         {
12808           /* Encode alpha as a grayscale JPEG blob */
12809
12810           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12811             exception);
12812           if (status == MagickFalse)
12813             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12814
12815           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12816             MagickPathExtent);
12817           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12818           jpeg_image_info->interlace=NoInterlace;
12819           if (logging != MagickFalse)
12820             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12821               "  Creating blob.");
12822           blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12823              jpeg_image,&length,
12824            exception);
12825           jng_alpha_sample_depth=8;
12826
12827           if (logging != MagickFalse)
12828             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12829               "  Successfully read jpeg_image into a blob, length=%.20g.",
12830               (double) length);
12831
12832         }
12833       /* Destroy JPEG image and image_info */
12834       jpeg_image=DestroyImage(jpeg_image);
12835       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12836       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12837     }
12838
12839   /* Write JHDR chunk */
12840   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12841   PNGType(chunk,mng_JHDR);
12842   LogPNGChunk(logging,mng_JHDR,16L);
12843   PNGLong(chunk+4,(png_uint_32) image->columns);
12844   PNGLong(chunk+8,(png_uint_32) image->rows);
12845   chunk[12]=jng_color_type;
12846   chunk[13]=8;  /* sample depth */
12847   chunk[14]=8; /*jng_image_compression_method */
12848   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12849   chunk[16]=jng_alpha_sample_depth;
12850   chunk[17]=jng_alpha_compression_method;
12851   chunk[18]=0; /*jng_alpha_filter_method */
12852   chunk[19]=0; /*jng_alpha_interlace_method */
12853   (void) WriteBlob(image,20,chunk);
12854   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12855   if (logging != MagickFalse)
12856     {
12857       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12858         "    JNG width:%15lu",(unsigned long) image->columns);
12859
12860       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12861         "    JNG height:%14lu",(unsigned long) image->rows);
12862
12863       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12864         "    JNG color type:%10d",jng_color_type);
12865
12866       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12867         "    JNG sample depth:%8d",8);
12868
12869       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12870         "    JNG compression:%9d",8);
12871
12872       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12873         "    JNG interlace:%11d",0);
12874
12875       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12876         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12877
12878       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12879         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12880
12881       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12882         "    JNG alpha filter:%8d",0);
12883
12884       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12885         "    JNG alpha interlace:%5d",0);
12886     }
12887
12888   /*
12889      Write leading ancillary chunks
12890   */
12891
12892   if (transparent != 0)
12893   {
12894     /*
12895       Write JNG bKGD chunk
12896     */
12897
12898     unsigned char
12899       blue,
12900       green,
12901       red;
12902
12903     ssize_t
12904       num_bytes;
12905
12906     if (jng_color_type == 8 || jng_color_type == 12)
12907       num_bytes=6L;
12908     else
12909       num_bytes=10L;
12910     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12911     PNGType(chunk,mng_bKGD);
12912     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12913     red=ScaleQuantumToChar(image->background_color.red);
12914     green=ScaleQuantumToChar(image->background_color.green);
12915     blue=ScaleQuantumToChar(image->background_color.blue);
12916     *(chunk+4)=0;
12917     *(chunk+5)=red;
12918     *(chunk+6)=0;
12919     *(chunk+7)=green;
12920     *(chunk+8)=0;
12921     *(chunk+9)=blue;
12922     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12923     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12924   }
12925
12926   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12927     {
12928       /*
12929         Write JNG sRGB chunk
12930       */
12931       (void) WriteBlobMSBULong(image,1L);
12932       PNGType(chunk,mng_sRGB);
12933       LogPNGChunk(logging,mng_sRGB,1L);
12934
12935       if (image->rendering_intent != UndefinedIntent)
12936         chunk[4]=(unsigned char)
12937           Magick_RenderingIntent_to_PNG_RenderingIntent(
12938           (image->rendering_intent));
12939
12940       else
12941         chunk[4]=(unsigned char)
12942           Magick_RenderingIntent_to_PNG_RenderingIntent(
12943           (PerceptualIntent));
12944
12945       (void) WriteBlob(image,5,chunk);
12946       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12947     }
12948   else
12949     {
12950       if (image->gamma != 0.0)
12951         {
12952           /*
12953              Write JNG gAMA chunk
12954           */
12955           (void) WriteBlobMSBULong(image,4L);
12956           PNGType(chunk,mng_gAMA);
12957           LogPNGChunk(logging,mng_gAMA,4L);
12958           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12959           (void) WriteBlob(image,8,chunk);
12960           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12961         }
12962
12963       if ((mng_info->equal_chrms == MagickFalse) &&
12964           (image->chromaticity.red_primary.x != 0.0))
12965         {
12966           PrimaryInfo
12967             primary;
12968
12969           /*
12970              Write JNG cHRM chunk
12971           */
12972           (void) WriteBlobMSBULong(image,32L);
12973           PNGType(chunk,mng_cHRM);
12974           LogPNGChunk(logging,mng_cHRM,32L);
12975           primary=image->chromaticity.white_point;
12976           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12977           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12978           primary=image->chromaticity.red_primary;
12979           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12980           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12981           primary=image->chromaticity.green_primary;
12982           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12983           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12984           primary=image->chromaticity.blue_primary;
12985           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12986           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12987           (void) WriteBlob(image,36,chunk);
12988           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12989         }
12990     }
12991
12992   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12993     {
12994       /*
12995          Write JNG pHYs chunk
12996       */
12997       (void) WriteBlobMSBULong(image,9L);
12998       PNGType(chunk,mng_pHYs);
12999       LogPNGChunk(logging,mng_pHYs,9L);
13000       if (image->units == PixelsPerInchResolution)
13001         {
13002           PNGLong(chunk+4,(png_uint_32)
13003             (image->resolution.x*100.0/2.54+0.5));
13004
13005           PNGLong(chunk+8,(png_uint_32)
13006             (image->resolution.y*100.0/2.54+0.5));
13007
13008           chunk[12]=1;
13009         }
13010
13011       else
13012         {
13013           if (image->units == PixelsPerCentimeterResolution)
13014             {
13015               PNGLong(chunk+4,(png_uint_32)
13016                 (image->resolution.x*100.0+0.5));
13017
13018               PNGLong(chunk+8,(png_uint_32)
13019                 (image->resolution.y*100.0+0.5));
13020
13021               chunk[12]=1;
13022             }
13023
13024           else
13025             {
13026               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13027               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13028               chunk[12]=0;
13029             }
13030         }
13031       (void) WriteBlob(image,13,chunk);
13032       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13033     }
13034
13035   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
13036     {
13037       /*
13038          Write JNG oFFs chunk
13039       */
13040       (void) WriteBlobMSBULong(image,9L);
13041       PNGType(chunk,mng_oFFs);
13042       LogPNGChunk(logging,mng_oFFs,9L);
13043       PNGsLong(chunk+4,(ssize_t) (image->page.x));
13044       PNGsLong(chunk+8,(ssize_t) (image->page.y));
13045       chunk[12]=0;
13046       (void) WriteBlob(image,13,chunk);
13047       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13048     }
13049
13050   if (transparent != 0)
13051     {
13052       if (jng_alpha_compression_method==0)
13053         {
13054           register ssize_t
13055             i;
13056
13057           size_t
13058             len;
13059
13060           /* Write IDAT chunk header */
13061           if (logging != MagickFalse)
13062             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13063               "  Write IDAT chunks from blob, length=%.20g.",(double)
13064               length);
13065
13066           /* Copy IDAT chunks */
13067           len=0;
13068           p=blob+8;
13069           for (i=8; i<(ssize_t) length; i+=len+12)
13070           {
13071             len=(((unsigned int) *(p    ) & 0xff) << 24) +
13072                 (((unsigned int) *(p + 1) & 0xff) << 16) +
13073                 (((unsigned int) *(p + 2) & 0xff) <<  8) +
13074                 (((unsigned int) *(p + 3) & 0xff)      ) ;
13075             p+=4;
13076
13077             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
13078               {
13079                 /* Found an IDAT chunk. */
13080                 (void) WriteBlobMSBULong(image,len);
13081                 LogPNGChunk(logging,mng_IDAT,len);
13082                 (void) WriteBlob(image,len+4,p);
13083                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
13084               }
13085
13086             else
13087               {
13088                 if (logging != MagickFalse)
13089                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13090                     "    Skipping %c%c%c%c chunk, length=%.20g.",
13091                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
13092               }
13093             p+=(8+len);
13094           }
13095         }
13096       else if (length != 0)
13097         {
13098           /* Write JDAA chunk header */
13099           if (logging != MagickFalse)
13100             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13101               "  Write JDAA chunk, length=%.20g.",(double) length);
13102           (void) WriteBlobMSBULong(image,(size_t) length);
13103           PNGType(chunk,mng_JDAA);
13104           LogPNGChunk(logging,mng_JDAA,length);
13105           /* Write JDAT chunk(s) data */
13106           (void) WriteBlob(image,4,chunk);
13107           (void) WriteBlob(image,length,blob);
13108           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
13109              (uInt) length));
13110         }
13111       blob=(unsigned char *) RelinquishMagickMemory(blob);
13112     }
13113
13114   /* Encode image as a JPEG blob */
13115   if (logging != MagickFalse)
13116     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13117       "  Creating jpeg_image_info.");
13118   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
13119   if (jpeg_image_info == (ImageInfo *) NULL)
13120     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13121
13122   if (logging != MagickFalse)
13123     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13124       "  Creating jpeg_image.");
13125
13126   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
13127   if (jpeg_image == (Image *) NULL)
13128     {
13129       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13130       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13131     }
13132   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13133
13134   (void) AcquireUniqueFilename(jpeg_image->filename);
13135   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
13136     jpeg_image->filename);
13137
13138   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
13139     exception);
13140
13141   if (logging != MagickFalse)
13142     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13143       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
13144       (double) jpeg_image->rows);
13145
13146   if (status == MagickFalse)
13147     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13148
13149   if (jng_color_type == 8 || jng_color_type == 12)
13150     jpeg_image_info->type=GrayscaleType;
13151
13152   jpeg_image_info->quality=jng_quality;
13153   jpeg_image->quality=jng_quality;
13154   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
13155   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
13156
13157   if (logging != MagickFalse)
13158     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13159       "  Creating blob.");
13160
13161   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13162     exception);
13163
13164   if (logging != MagickFalse)
13165     {
13166       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13167         "  Successfully read jpeg_image into a blob, length=%.20g.",
13168         (double) length);
13169
13170       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13171         "  Write JDAT chunk, length=%.20g.",(double) length);
13172     }
13173
13174   /* Write JDAT chunk(s) */
13175   (void) WriteBlobMSBULong(image,(size_t) length);
13176   PNGType(chunk,mng_JDAT);
13177   LogPNGChunk(logging,mng_JDAT,length);
13178   (void) WriteBlob(image,4,chunk);
13179   (void) WriteBlob(image,length,blob);
13180   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13181
13182   jpeg_image=DestroyImage(jpeg_image);
13183   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13184   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13185   blob=(unsigned char *) RelinquishMagickMemory(blob);
13186
13187   /* Write IEND chunk */
13188   (void) WriteBlobMSBULong(image,0L);
13189   PNGType(chunk,mng_IEND);
13190   LogPNGChunk(logging,mng_IEND,0);
13191   (void) WriteBlob(image,4,chunk);
13192   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13193
13194   if (logging != MagickFalse)
13195     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13196       "  exit WriteOneJNGImage()");
13197
13198   return(status);
13199 }
13200
13201 /*
13202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13203 %                                                                             %
13204 %                                                                             %
13205 %                                                                             %
13206 %   W r i t e J N G I m a g e                                                 %
13207 %                                                                             %
13208 %                                                                             %
13209 %                                                                             %
13210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13211 %
13212 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13213 %
13214 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
13215 %
13216 %  The format of the WriteJNGImage method is:
13217 %
13218 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13219 %        Image *image,ExceptionInfo *exception)
13220 %
13221 %  A description of each parameter follows:
13222 %
13223 %    o image_info: the image info.
13224 %
13225 %    o image:  The image.
13226 %
13227 %    o exception: return any errors or warnings in this structure.
13228 %
13229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13230 */
13231 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13232   Image *image, ExceptionInfo *exception)
13233 {
13234   MagickBooleanType
13235     logging,
13236     status;
13237
13238   MngInfo
13239     *mng_info;
13240
13241   /*
13242     Open image file.
13243   */
13244   assert(image_info != (const ImageInfo *) NULL);
13245   assert(image_info->signature == MagickCoreSignature);
13246   assert(image != (Image *) NULL);
13247   assert(image->signature == MagickCoreSignature);
13248   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13249   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13250   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13251   if (status == MagickFalse)
13252     return(status);
13253   if ((image->columns > 65535UL) || (image->rows > 65535UL))
13254     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13255
13256   /*
13257     Allocate a MngInfo structure.
13258   */
13259   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13260   if (mng_info == (MngInfo *) NULL)
13261     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13262   /*
13263     Initialize members of the MngInfo structure.
13264   */
13265   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13266   mng_info->image=image;
13267
13268   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13269
13270   status=WriteOneJNGImage(mng_info,image_info,image,exception);
13271   mng_info=MngInfoFreeStruct(mng_info);
13272   (void) CloseBlob(image);
13273
13274   (void) CatchImageException(image);
13275   if (logging != MagickFalse)
13276     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13277   return(status);
13278 }
13279 #endif
13280
13281 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13282   Image *image, ExceptionInfo *exception)
13283 {
13284   Image
13285     *next_image;
13286
13287   MagickBooleanType
13288     status;
13289
13290   volatile MagickBooleanType
13291     logging;
13292
13293   MngInfo
13294     *mng_info;
13295
13296   int
13297     image_count,
13298     need_iterations,
13299     need_matte;
13300
13301   volatile int
13302 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13303     defined(PNG_MNG_FEATURES_SUPPORTED)
13304     need_local_plte,
13305 #endif
13306     all_images_are_gray,
13307     need_defi,
13308     use_global_plte;
13309
13310   register ssize_t
13311     i;
13312
13313   unsigned char
13314     chunk[800];
13315
13316   volatile unsigned int
13317     write_jng,
13318     write_mng;
13319
13320   volatile size_t
13321     scene;
13322
13323   size_t
13324     final_delay=0,
13325     initial_delay;
13326
13327 #if (PNG_LIBPNG_VER < 10200)
13328     if (image_info->verbose)
13329       printf("Your PNG library (libpng-%s) is rather old.\n",
13330          PNG_LIBPNG_VER_STRING);
13331 #endif
13332
13333   /*
13334     Open image file.
13335   */
13336   assert(image_info != (const ImageInfo *) NULL);
13337   assert(image_info->signature == MagickCoreSignature);
13338   assert(image != (Image *) NULL);
13339   assert(image->signature == MagickCoreSignature);
13340   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13341   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13342   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13343   if (status == MagickFalse)
13344     return(status);
13345
13346   /*
13347     Allocate a MngInfo structure.
13348   */
13349   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13350   if (mng_info == (MngInfo *) NULL)
13351     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13352   /*
13353     Initialize members of the MngInfo structure.
13354   */
13355   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13356   mng_info->image=image;
13357   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13358
13359   /*
13360    * See if user has requested a specific PNG subformat to be used
13361    * for all of the PNGs in the MNG being written, e.g.,
13362    *
13363    *    convert *.png png8:animation.mng
13364    *
13365    * To do: check -define png:bit_depth and png:color_type as well,
13366    * or perhaps use mng:bit_depth and mng:color_type instead for
13367    * global settings.
13368    */
13369
13370   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13371   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13372   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13373
13374   write_jng=MagickFalse;
13375   if (image_info->compression == JPEGCompression)
13376     write_jng=MagickTrue;
13377
13378   mng_info->adjoin=image_info->adjoin &&
13379     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13380
13381   if (logging != MagickFalse)
13382     {
13383       /* Log some info about the input */
13384       Image
13385         *p;
13386
13387       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13388         "  Checking input image(s)\n"
13389         "    Image_info depth: %.20g,    Type: %d",
13390         (double) image_info->depth, image_info->type);
13391
13392       scene=0;
13393       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13394       {
13395
13396         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13397            "    Scene: %.20g\n,   Image depth: %.20g",
13398            (double) scene++, (double) p->depth);
13399
13400         if (p->alpha_trait != UndefinedPixelTrait)
13401           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13402             "      Matte: True");
13403
13404         else
13405           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13406             "      Matte: False");
13407
13408         if (p->storage_class == PseudoClass)
13409           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13410             "      Storage class: PseudoClass");
13411
13412         else
13413           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13414             "      Storage class: DirectClass");
13415
13416         if (p->colors)
13417           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13418             "      Number of colors: %.20g",(double) p->colors);
13419
13420         else
13421           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13422             "      Number of colors: unspecified");
13423
13424         if (mng_info->adjoin == MagickFalse)
13425           break;
13426       }
13427     }
13428
13429   use_global_plte=MagickFalse;
13430   all_images_are_gray=MagickFalse;
13431 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13432   need_local_plte=MagickTrue;
13433 #endif
13434   need_defi=MagickFalse;
13435   need_matte=MagickFalse;
13436   mng_info->framing_mode=1;
13437   mng_info->old_framing_mode=1;
13438
13439   if (write_mng)
13440       if (image_info->page != (char *) NULL)
13441         {
13442           /*
13443             Determine image bounding box.
13444           */
13445           SetGeometry(image,&mng_info->page);
13446           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13447             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13448         }
13449   if (write_mng)
13450     {
13451       unsigned int
13452         need_geom;
13453
13454       unsigned short
13455         red,
13456         green,
13457         blue;
13458
13459       const char *
13460         option;
13461
13462       mng_info->page=image->page;
13463       need_geom=MagickTrue;
13464       if (mng_info->page.width || mng_info->page.height)
13465          need_geom=MagickFalse;
13466       /*
13467         Check all the scenes.
13468       */
13469       initial_delay=image->delay;
13470       need_iterations=MagickFalse;
13471       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13472       mng_info->equal_physs=MagickTrue,
13473       mng_info->equal_gammas=MagickTrue;
13474       mng_info->equal_srgbs=MagickTrue;
13475       mng_info->equal_backgrounds=MagickTrue;
13476       image_count=0;
13477 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13478     defined(PNG_MNG_FEATURES_SUPPORTED)
13479       all_images_are_gray=MagickTrue;
13480       mng_info->equal_palettes=MagickFalse;
13481       need_local_plte=MagickFalse;
13482 #endif
13483       for (next_image=image; next_image != (Image *) NULL; )
13484       {
13485         if (need_geom)
13486           {
13487             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13488               mng_info->page.width=next_image->columns+next_image->page.x;
13489
13490             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13491               mng_info->page.height=next_image->rows+next_image->page.y;
13492           }
13493
13494         if (next_image->page.x || next_image->page.y)
13495           need_defi=MagickTrue;
13496
13497         if (next_image->alpha_trait != UndefinedPixelTrait)
13498           need_matte=MagickTrue;
13499
13500         if ((int) next_image->dispose >= BackgroundDispose)
13501           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13502                next_image->page.x || next_image->page.y ||
13503               ((next_image->columns < mng_info->page.width) &&
13504                (next_image->rows < mng_info->page.height)))
13505             mng_info->need_fram=MagickTrue;
13506
13507         if (next_image->iterations)
13508           need_iterations=MagickTrue;
13509
13510         final_delay=next_image->delay;
13511
13512         if (final_delay != initial_delay || final_delay > 1UL*
13513            next_image->ticks_per_second)
13514           mng_info->need_fram=1;
13515
13516 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13517     defined(PNG_MNG_FEATURES_SUPPORTED)
13518         /*
13519           check for global palette possibility.
13520         */
13521         if (image->alpha_trait != UndefinedPixelTrait)
13522            need_local_plte=MagickTrue;
13523
13524         if (need_local_plte == 0)
13525           {
13526             if (SetImageGray(image,exception) == MagickFalse)
13527               all_images_are_gray=MagickFalse;
13528             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13529             if (use_global_plte == 0)
13530               use_global_plte=mng_info->equal_palettes;
13531             need_local_plte=!mng_info->equal_palettes;
13532           }
13533 #endif
13534         if (GetNextImageInList(next_image) != (Image *) NULL)
13535           {
13536             if (next_image->background_color.red !=
13537                 next_image->next->background_color.red ||
13538                 next_image->background_color.green !=
13539                 next_image->next->background_color.green ||
13540                 next_image->background_color.blue !=
13541                 next_image->next->background_color.blue)
13542               mng_info->equal_backgrounds=MagickFalse;
13543
13544             if (next_image->gamma != next_image->next->gamma)
13545               mng_info->equal_gammas=MagickFalse;
13546
13547             if (next_image->rendering_intent !=
13548                 next_image->next->rendering_intent)
13549               mng_info->equal_srgbs=MagickFalse;
13550
13551             if ((next_image->units != next_image->next->units) ||
13552                 (next_image->resolution.x != next_image->next->resolution.x) ||
13553                 (next_image->resolution.y != next_image->next->resolution.y))
13554               mng_info->equal_physs=MagickFalse;
13555
13556             if (mng_info->equal_chrms)
13557               {
13558                 if (next_image->chromaticity.red_primary.x !=
13559                     next_image->next->chromaticity.red_primary.x ||
13560                     next_image->chromaticity.red_primary.y !=
13561                     next_image->next->chromaticity.red_primary.y ||
13562                     next_image->chromaticity.green_primary.x !=
13563                     next_image->next->chromaticity.green_primary.x ||
13564                     next_image->chromaticity.green_primary.y !=
13565                     next_image->next->chromaticity.green_primary.y ||
13566                     next_image->chromaticity.blue_primary.x !=
13567                     next_image->next->chromaticity.blue_primary.x ||
13568                     next_image->chromaticity.blue_primary.y !=
13569                     next_image->next->chromaticity.blue_primary.y ||
13570                     next_image->chromaticity.white_point.x !=
13571                     next_image->next->chromaticity.white_point.x ||
13572                     next_image->chromaticity.white_point.y !=
13573                     next_image->next->chromaticity.white_point.y)
13574                   mng_info->equal_chrms=MagickFalse;
13575               }
13576           }
13577         image_count++;
13578         next_image=GetNextImageInList(next_image);
13579       }
13580       if (image_count < 2)
13581         {
13582           mng_info->equal_backgrounds=MagickFalse;
13583           mng_info->equal_chrms=MagickFalse;
13584           mng_info->equal_gammas=MagickFalse;
13585           mng_info->equal_srgbs=MagickFalse;
13586           mng_info->equal_physs=MagickFalse;
13587           use_global_plte=MagickFalse;
13588 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13589           need_local_plte=MagickTrue;
13590 #endif
13591           need_iterations=MagickFalse;
13592         }
13593
13594      if (mng_info->need_fram == MagickFalse)
13595        {
13596          /*
13597            Only certain framing rates 100/n are exactly representable without
13598            the FRAM chunk but we'll allow some slop in VLC files
13599          */
13600          if (final_delay == 0)
13601            {
13602              if (need_iterations != MagickFalse)
13603                {
13604                  /*
13605                    It's probably a GIF with loop; don't run it *too* fast.
13606                  */
13607                  if (mng_info->adjoin)
13608                    {
13609                      final_delay=10;
13610                      (void) ThrowMagickException(exception,GetMagickModule(),
13611                        CoderWarning,
13612                        "input has zero delay between all frames; assuming",
13613                        " 10 cs `%s'","");
13614                    }
13615                }
13616              else
13617                mng_info->ticks_per_second=0;
13618            }
13619          if (final_delay != 0)
13620            mng_info->ticks_per_second=(png_uint_32)
13621               (image->ticks_per_second/final_delay);
13622          if (final_delay > 50)
13623            mng_info->ticks_per_second=2;
13624
13625          if (final_delay > 75)
13626            mng_info->ticks_per_second=1;
13627
13628          if (final_delay > 125)
13629            mng_info->need_fram=MagickTrue;
13630
13631          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13632             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13633             (final_delay != 25) && (final_delay != 50) &&
13634             (final_delay != (size_t) image->ticks_per_second))
13635            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13636        }
13637
13638      if (mng_info->need_fram != MagickFalse)
13639         mng_info->ticks_per_second=image->ticks_per_second;
13640      /*
13641         If pseudocolor, we should also check to see if all the
13642         palettes are identical and write a global PLTE if they are.
13643         ../glennrp Feb 99.
13644      */
13645      /*
13646         Write the MNG version 1.0 signature and MHDR chunk.
13647      */
13648      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13649      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13650      PNGType(chunk,mng_MHDR);
13651      LogPNGChunk(logging,mng_MHDR,28L);
13652      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13653      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13654      PNGLong(chunk+12,mng_info->ticks_per_second);
13655      PNGLong(chunk+16,0L);  /* layer count=unknown */
13656      PNGLong(chunk+20,0L);  /* frame count=unknown */
13657      PNGLong(chunk+24,0L);  /* play time=unknown   */
13658      if (write_jng)
13659        {
13660          if (need_matte)
13661            {
13662              if (need_defi || mng_info->need_fram || use_global_plte)
13663                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13664
13665              else
13666                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13667            }
13668
13669          else
13670            {
13671              if (need_defi || mng_info->need_fram || use_global_plte)
13672                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13673
13674              else
13675                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13676            }
13677        }
13678
13679      else
13680        {
13681          if (need_matte)
13682            {
13683              if (need_defi || mng_info->need_fram || use_global_plte)
13684                PNGLong(chunk+28,11L);    /* simplicity=LC */
13685
13686              else
13687                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13688            }
13689
13690          else
13691            {
13692              if (need_defi || mng_info->need_fram || use_global_plte)
13693                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13694
13695              else
13696                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13697            }
13698        }
13699      (void) WriteBlob(image,32,chunk);
13700      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13701      option=GetImageOption(image_info,"mng:need-cacheoff");
13702      if (option != (const char *) NULL)
13703        {
13704          size_t
13705            length;
13706          /*
13707            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13708          */
13709          PNGType(chunk,mng_nEED);
13710          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13711          (void) WriteBlobMSBULong(image,(size_t) length);
13712          LogPNGChunk(logging,mng_nEED,(size_t) length);
13713          length+=4;
13714          (void) WriteBlob(image,length,chunk);
13715          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13716        }
13717      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13718          (GetNextImageInList(image) != (Image *) NULL) &&
13719          (image->iterations != 1))
13720        {
13721          /*
13722            Write MNG TERM chunk
13723          */
13724          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13725          PNGType(chunk,mng_TERM);
13726          LogPNGChunk(logging,mng_TERM,10L);
13727          chunk[4]=3;  /* repeat animation */
13728          chunk[5]=0;  /* show last frame when done */
13729          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13730             final_delay/MagickMax(image->ticks_per_second,1)));
13731
13732          if (image->iterations == 0)
13733            PNGLong(chunk+10,PNG_UINT_31_MAX);
13734
13735          else
13736            PNGLong(chunk+10,(png_uint_32) image->iterations);
13737
13738          if (logging != MagickFalse)
13739            {
13740              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13741                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13742               final_delay/MagickMax(image->ticks_per_second,1)));
13743
13744              if (image->iterations == 0)
13745                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13746                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13747
13748              else
13749                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13750                  "     Image iterations: %.20g",(double) image->iterations);
13751            }
13752          (void) WriteBlob(image,14,chunk);
13753          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13754        }
13755      /*
13756        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13757      */
13758      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13759           mng_info->equal_srgbs)
13760        {
13761          /*
13762            Write MNG sRGB chunk
13763          */
13764          (void) WriteBlobMSBULong(image,1L);
13765          PNGType(chunk,mng_sRGB);
13766          LogPNGChunk(logging,mng_sRGB,1L);
13767
13768          if (image->rendering_intent != UndefinedIntent)
13769            chunk[4]=(unsigned char)
13770              Magick_RenderingIntent_to_PNG_RenderingIntent(
13771              (image->rendering_intent));
13772
13773          else
13774            chunk[4]=(unsigned char)
13775              Magick_RenderingIntent_to_PNG_RenderingIntent(
13776                (PerceptualIntent));
13777
13778          (void) WriteBlob(image,5,chunk);
13779          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13780          mng_info->have_write_global_srgb=MagickTrue;
13781        }
13782
13783      else
13784        {
13785          if (image->gamma && mng_info->equal_gammas)
13786            {
13787              /*
13788                 Write MNG gAMA chunk
13789              */
13790              (void) WriteBlobMSBULong(image,4L);
13791              PNGType(chunk,mng_gAMA);
13792              LogPNGChunk(logging,mng_gAMA,4L);
13793              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13794              (void) WriteBlob(image,8,chunk);
13795              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13796              mng_info->have_write_global_gama=MagickTrue;
13797            }
13798          if (mng_info->equal_chrms)
13799            {
13800              PrimaryInfo
13801                primary;
13802
13803              /*
13804                 Write MNG cHRM chunk
13805              */
13806              (void) WriteBlobMSBULong(image,32L);
13807              PNGType(chunk,mng_cHRM);
13808              LogPNGChunk(logging,mng_cHRM,32L);
13809              primary=image->chromaticity.white_point;
13810              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13811              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13812              primary=image->chromaticity.red_primary;
13813              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13814              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13815              primary=image->chromaticity.green_primary;
13816              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13817              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13818              primary=image->chromaticity.blue_primary;
13819              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13820              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13821              (void) WriteBlob(image,36,chunk);
13822              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13823              mng_info->have_write_global_chrm=MagickTrue;
13824            }
13825        }
13826      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13827        {
13828          /*
13829             Write MNG pHYs chunk
13830          */
13831          (void) WriteBlobMSBULong(image,9L);
13832          PNGType(chunk,mng_pHYs);
13833          LogPNGChunk(logging,mng_pHYs,9L);
13834
13835          if (image->units == PixelsPerInchResolution)
13836            {
13837              PNGLong(chunk+4,(png_uint_32)
13838                (image->resolution.x*100.0/2.54+0.5));
13839
13840              PNGLong(chunk+8,(png_uint_32)
13841                (image->resolution.y*100.0/2.54+0.5));
13842
13843              chunk[12]=1;
13844            }
13845
13846          else
13847            {
13848              if (image->units == PixelsPerCentimeterResolution)
13849                {
13850                  PNGLong(chunk+4,(png_uint_32)
13851                    (image->resolution.x*100.0+0.5));
13852
13853                  PNGLong(chunk+8,(png_uint_32)
13854                    (image->resolution.y*100.0+0.5));
13855
13856                  chunk[12]=1;
13857                }
13858
13859              else
13860                {
13861                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13862                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13863                  chunk[12]=0;
13864                }
13865            }
13866          (void) WriteBlob(image,13,chunk);
13867          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13868        }
13869      /*
13870        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13871        or does not cover the entire frame.
13872      */
13873      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13874          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13875          (image->page.width+image->page.x < mng_info->page.width))
13876          || (image->page.height && (image->page.height+image->page.y
13877          < mng_info->page.height))))
13878        {
13879          (void) WriteBlobMSBULong(image,6L);
13880          PNGType(chunk,mng_BACK);
13881          LogPNGChunk(logging,mng_BACK,6L);
13882          red=ScaleQuantumToShort(image->background_color.red);
13883          green=ScaleQuantumToShort(image->background_color.green);
13884          blue=ScaleQuantumToShort(image->background_color.blue);
13885          PNGShort(chunk+4,red);
13886          PNGShort(chunk+6,green);
13887          PNGShort(chunk+8,blue);
13888          (void) WriteBlob(image,10,chunk);
13889          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13890          if (mng_info->equal_backgrounds)
13891            {
13892              (void) WriteBlobMSBULong(image,6L);
13893              PNGType(chunk,mng_bKGD);
13894              LogPNGChunk(logging,mng_bKGD,6L);
13895              (void) WriteBlob(image,10,chunk);
13896              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13897            }
13898        }
13899
13900 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13901      if ((need_local_plte == MagickFalse) &&
13902          (image->storage_class == PseudoClass) &&
13903          (all_images_are_gray == MagickFalse))
13904        {
13905          size_t
13906            data_length;
13907
13908          /*
13909            Write MNG PLTE chunk
13910          */
13911          data_length=3*image->colors;
13912          (void) WriteBlobMSBULong(image,data_length);
13913          PNGType(chunk,mng_PLTE);
13914          LogPNGChunk(logging,mng_PLTE,data_length);
13915
13916          for (i=0; i < (ssize_t) image->colors; i++)
13917          {
13918            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13919              image->colormap[i].red) & 0xff);
13920            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13921              image->colormap[i].green) & 0xff);
13922            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13923              image->colormap[i].blue) & 0xff);
13924          }
13925
13926          (void) WriteBlob(image,data_length+4,chunk);
13927          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13928          mng_info->have_write_global_plte=MagickTrue;
13929        }
13930 #endif
13931     }
13932   scene=0;
13933   mng_info->delay=0;
13934 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13935     defined(PNG_MNG_FEATURES_SUPPORTED)
13936   mng_info->equal_palettes=MagickFalse;
13937 #endif
13938   do
13939   {
13940     if (mng_info->adjoin)
13941     {
13942 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13943     defined(PNG_MNG_FEATURES_SUPPORTED)
13944     /*
13945       If we aren't using a global palette for the entire MNG, check to
13946       see if we can use one for two or more consecutive images.
13947     */
13948     if (need_local_plte && use_global_plte && !all_images_are_gray)
13949       {
13950         if (mng_info->IsPalette)
13951           {
13952             /*
13953               When equal_palettes is true, this image has the same palette
13954               as the previous PseudoClass image
13955             */
13956             mng_info->have_write_global_plte=mng_info->equal_palettes;
13957             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13958             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13959               {
13960                 /*
13961                   Write MNG PLTE chunk
13962                 */
13963                 size_t
13964                   data_length;
13965
13966                 data_length=3*image->colors;
13967                 (void) WriteBlobMSBULong(image,data_length);
13968                 PNGType(chunk,mng_PLTE);
13969                 LogPNGChunk(logging,mng_PLTE,data_length);
13970
13971                 for (i=0; i < (ssize_t) image->colors; i++)
13972                 {
13973                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13974                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13975                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13976                 }
13977
13978                 (void) WriteBlob(image,data_length+4,chunk);
13979                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13980                    (uInt) (data_length+4)));
13981                 mng_info->have_write_global_plte=MagickTrue;
13982               }
13983           }
13984         else
13985           mng_info->have_write_global_plte=MagickFalse;
13986       }
13987 #endif
13988     if (need_defi)
13989       {
13990         ssize_t
13991           previous_x,
13992           previous_y;
13993
13994         if (scene)
13995           {
13996             previous_x=mng_info->page.x;
13997             previous_y=mng_info->page.y;
13998           }
13999         else
14000           {
14001             previous_x=0;
14002             previous_y=0;
14003           }
14004         mng_info->page=image->page;
14005         if ((mng_info->page.x !=  previous_x) ||
14006             (mng_info->page.y != previous_y))
14007           {
14008              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
14009              PNGType(chunk,mng_DEFI);
14010              LogPNGChunk(logging,mng_DEFI,12L);
14011              chunk[4]=0; /* object 0 MSB */
14012              chunk[5]=0; /* object 0 LSB */
14013              chunk[6]=0; /* visible  */
14014              chunk[7]=0; /* abstract */
14015              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
14016              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
14017              (void) WriteBlob(image,16,chunk);
14018              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
14019           }
14020       }
14021     }
14022
14023    mng_info->write_mng=write_mng;
14024
14025    if ((int) image->dispose >= 3)
14026      mng_info->framing_mode=3;
14027
14028    if (mng_info->need_fram && mng_info->adjoin &&
14029        ((image->delay != mng_info->delay) ||
14030         (mng_info->framing_mode != mng_info->old_framing_mode)))
14031      {
14032        if (image->delay == mng_info->delay)
14033          {
14034            /*
14035              Write a MNG FRAM chunk with the new framing mode.
14036            */
14037            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
14038            PNGType(chunk,mng_FRAM);
14039            LogPNGChunk(logging,mng_FRAM,1L);
14040            chunk[4]=(unsigned char) mng_info->framing_mode;
14041            (void) WriteBlob(image,5,chunk);
14042            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
14043          }
14044        else
14045          {
14046            /*
14047              Write a MNG FRAM chunk with the delay.
14048            */
14049            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
14050            PNGType(chunk,mng_FRAM);
14051            LogPNGChunk(logging,mng_FRAM,10L);
14052            chunk[4]=(unsigned char) mng_info->framing_mode;
14053            chunk[5]=0;  /* frame name separator (no name) */
14054            chunk[6]=2;  /* flag for changing default delay */
14055            chunk[7]=0;  /* flag for changing frame timeout */
14056            chunk[8]=0;  /* flag for changing frame clipping */
14057            chunk[9]=0;  /* flag for changing frame sync_id */
14058            PNGLong(chunk+10,(png_uint_32)
14059              ((mng_info->ticks_per_second*
14060              image->delay)/MagickMax(image->ticks_per_second,1)));
14061            (void) WriteBlob(image,14,chunk);
14062            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
14063            mng_info->delay=(png_uint_32) image->delay;
14064          }
14065        mng_info->old_framing_mode=mng_info->framing_mode;
14066      }
14067
14068 #if defined(JNG_SUPPORTED)
14069    if (image_info->compression == JPEGCompression)
14070      {
14071        ImageInfo
14072          *write_info;
14073
14074        if (logging != MagickFalse)
14075          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14076            "  Writing JNG object.");
14077        /* To do: specify the desired alpha compression method. */
14078        write_info=CloneImageInfo(image_info);
14079        write_info->compression=UndefinedCompression;
14080        status=WriteOneJNGImage(mng_info,write_info,image,exception);
14081        write_info=DestroyImageInfo(write_info);
14082      }
14083    else
14084 #endif
14085      {
14086        if (logging != MagickFalse)
14087          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
14088            "  Writing PNG object.");
14089
14090        mng_info->need_blob = MagickFalse;
14091        mng_info->ping_preserve_colormap = MagickFalse;
14092
14093        /* We don't want any ancillary chunks written */
14094        mng_info->ping_exclude_bKGD=MagickTrue;
14095        mng_info->ping_exclude_caNv=MagickTrue;
14096        mng_info->ping_exclude_cHRM=MagickTrue;
14097        mng_info->ping_exclude_date=MagickTrue;
14098        mng_info->ping_exclude_EXIF=MagickTrue;
14099        mng_info->ping_exclude_gAMA=MagickTrue;
14100        mng_info->ping_exclude_iCCP=MagickTrue;
14101        /* mng_info->ping_exclude_iTXt=MagickTrue; */
14102        mng_info->ping_exclude_oFFs=MagickTrue;
14103        mng_info->ping_exclude_pHYs=MagickTrue;
14104        mng_info->ping_exclude_sRGB=MagickTrue;
14105        mng_info->ping_exclude_tEXt=MagickTrue;
14106        mng_info->ping_exclude_tRNS=MagickTrue;
14107        mng_info->ping_exclude_zCCP=MagickTrue;
14108        mng_info->ping_exclude_zTXt=MagickTrue;
14109
14110        status=WriteOnePNGImage(mng_info,image_info,image,exception);
14111      }
14112
14113     if (status == MagickFalse)
14114       {
14115         mng_info=MngInfoFreeStruct(mng_info);
14116         (void) CloseBlob(image);
14117         return(MagickFalse);
14118       }
14119     (void) CatchImageException(image);
14120     if (GetNextImageInList(image) == (Image *) NULL)
14121       break;
14122     image=SyncNextImageInList(image);
14123     status=SetImageProgress(image,SaveImagesTag,scene++,
14124       GetImageListLength(image));
14125
14126     if (status == MagickFalse)
14127       break;
14128
14129   } while (mng_info->adjoin);
14130
14131   if (write_mng)
14132     {
14133       while (GetPreviousImageInList(image) != (Image *) NULL)
14134         image=GetPreviousImageInList(image);
14135       /*
14136         Write the MEND chunk.
14137       */
14138       (void) WriteBlobMSBULong(image,0x00000000L);
14139       PNGType(chunk,mng_MEND);
14140       LogPNGChunk(logging,mng_MEND,0L);
14141       (void) WriteBlob(image,4,chunk);
14142       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
14143     }
14144   /*
14145     Relinquish resources.
14146   */
14147   (void) CloseBlob(image);
14148   mng_info=MngInfoFreeStruct(mng_info);
14149
14150   if (logging != MagickFalse)
14151     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
14152
14153   return(MagickTrue);
14154 }
14155 #else /* PNG_LIBPNG_VER > 10011 */
14156
14157 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14158   Image *image)
14159 {
14160   (void) image;
14161   printf("Your PNG library is too old: You have libpng-%s\n",
14162      PNG_LIBPNG_VER_STRING);
14163
14164   ThrowBinaryException(CoderError,"PNG library is too old",
14165      image_info->filename);
14166 }
14167
14168 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14169   Image *image)
14170 {
14171   return(WritePNGImage(image_info,image));
14172 }
14173 #endif /* PNG_LIBPNG_VER > 10011 */
14174 #endif