]> granicus.if.org Git - imagemagick/blob - coders/png.c
Removed many redundant checks on chunk length before RelinquishMagickMemory()
[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_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
561 static const png_byte mng_vpAg[5]={118, 112,  65, 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
567 #if defined(JNG_SUPPORTED)
568 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
569 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
570 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
571 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
572 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
573 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
574 #endif
575
576 #if 0
577 /* Other known chunks that are not yet supported by ImageMagick: */
578 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
579 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
580 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
581 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
582 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
583 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
584 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
585 #endif
586
587 typedef struct _MngBox
588 {
589   long
590     left,
591     right,
592     top,
593     bottom;
594 } MngBox;
595
596 typedef struct _MngPair
597 {
598   volatile long
599     a,
600     b;
601 } MngPair;
602
603 #ifdef MNG_OBJECT_BUFFERS
604 typedef struct _MngBuffer
605 {
606
607   size_t
608     height,
609     width;
610
611   Image
612     *image;
613
614   png_color
615     plte[256];
616
617   int
618     reference_count;
619
620   unsigned char
621     alpha_sample_depth,
622     compression_method,
623     color_type,
624     concrete,
625     filter_method,
626     frozen,
627     image_type,
628     interlace_method,
629     pixel_sample_depth,
630     plte_length,
631     sample_depth,
632     viewable;
633 } MngBuffer;
634 #endif
635
636 typedef struct _MngInfo
637 {
638
639 #ifdef MNG_OBJECT_BUFFERS
640   MngBuffer
641     *ob[MNG_MAX_OBJECTS];
642 #endif
643
644   Image *
645     image;
646
647   RectangleInfo
648     page;
649
650   int
651     adjoin,
652 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
653     bytes_in_read_buffer,
654     found_empty_plte,
655 #endif
656     equal_backgrounds,
657     equal_chrms,
658     equal_gammas,
659 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
660     defined(PNG_MNG_FEATURES_SUPPORTED)
661     equal_palettes,
662 #endif
663     equal_physs,
664     equal_srgbs,
665     framing_mode,
666     have_global_bkgd,
667     have_global_chrm,
668     have_global_gama,
669     have_global_phys,
670     have_global_sbit,
671     have_global_srgb,
672     have_saved_bkgd_index,
673     have_write_global_chrm,
674     have_write_global_gama,
675     have_write_global_plte,
676     have_write_global_srgb,
677     need_fram,
678     object_id,
679     old_framing_mode,
680     saved_bkgd_index;
681
682   int
683     new_number_colors;
684
685   ssize_t
686     image_found,
687     loop_count[256],
688     loop_iteration[256],
689     scenes_found,
690     x_off[MNG_MAX_OBJECTS],
691     y_off[MNG_MAX_OBJECTS];
692
693   MngBox
694     clip,
695     frame,
696     image_box,
697     object_clip[MNG_MAX_OBJECTS];
698
699   unsigned char
700     /* These flags could be combined into one byte */
701     exists[MNG_MAX_OBJECTS],
702     frozen[MNG_MAX_OBJECTS],
703     loop_active[256],
704     invisible[MNG_MAX_OBJECTS],
705     viewable[MNG_MAX_OBJECTS];
706
707   MagickOffsetType
708     loop_jump[256];
709
710   png_colorp
711     global_plte;
712
713   png_color_8
714     global_sbit;
715
716   png_byte
717 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
718     read_buffer[8],
719 #endif
720     global_trns[256];
721
722   float
723     global_gamma;
724
725   ChromaticityInfo
726     global_chrm;
727
728   RenderingIntent
729     global_srgb_intent;
730
731   unsigned long
732     delay,
733     global_plte_length,
734     global_trns_length,
735     global_x_pixels_per_unit,
736     global_y_pixels_per_unit,
737     mng_width,
738     mng_height,
739     ticks_per_second;
740
741   MagickBooleanType
742     need_blob;
743
744   unsigned int
745     IsPalette,
746     global_phys_unit_type,
747     basi_warning,
748     clon_warning,
749     dhdr_warning,
750     jhdr_warning,
751     magn_warning,
752     past_warning,
753     phyg_warning,
754     phys_warning,
755     sbit_warning,
756     show_warning,
757     mng_type,
758     write_mng,
759     write_png_colortype,
760     write_png_depth,
761     write_png_compression_level,
762     write_png_compression_strategy,
763     write_png_compression_filter,
764     write_png8,
765     write_png24,
766     write_png32,
767     write_png48,
768     write_png64;
769
770 #ifdef MNG_BASI_SUPPORTED
771   unsigned long
772     basi_width,
773     basi_height;
774
775   unsigned int
776     basi_depth,
777     basi_color_type,
778     basi_compression_method,
779     basi_filter_type,
780     basi_interlace_method,
781     basi_red,
782     basi_green,
783     basi_blue,
784     basi_alpha,
785     basi_viewable;
786 #endif
787
788   png_uint_16
789     magn_first,
790     magn_last,
791     magn_mb,
792     magn_ml,
793     magn_mr,
794     magn_mt,
795     magn_mx,
796     magn_my,
797     magn_methx,
798     magn_methy;
799
800   PixelInfo
801     mng_global_bkgd;
802
803   /* Added at version 6.6.6-7 */
804   MagickBooleanType
805     ping_exclude_bKGD,
806     ping_exclude_cHRM,
807     ping_exclude_date,
808     ping_exclude_eXIf,
809     ping_exclude_EXIF,
810     ping_exclude_gAMA,
811     ping_exclude_iCCP,
812     /* ping_exclude_iTXt, */
813     ping_exclude_oFFs,
814     ping_exclude_pHYs,
815     ping_exclude_sRGB,
816     ping_exclude_tEXt,
817     ping_exclude_tRNS,
818     ping_exclude_vpAg,
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_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
983 {
984   switch (intent)
985   {
986     case PerceptualIntent:
987        return 0;
988
989     case RelativeIntent:
990        return 1;
991
992     case SaturationIntent:
993        return 2;
994
995     case AbsoluteIntent:
996        return 3;
997
998     default:
999        return -1;
1000   }
1001 }
1002
1003 static RenderingIntent
1004 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
1005 {
1006   switch (ping_intent)
1007   {
1008     case 0:
1009       return PerceptualIntent;
1010
1011     case 1:
1012       return RelativeIntent;
1013
1014     case 2:
1015       return SaturationIntent;
1016
1017     case 3:
1018       return AbsoluteIntent;
1019
1020     default:
1021       return UndefinedIntent;
1022     }
1023 }
1024
1025 static const char *
1026 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1027 {
1028   switch (ping_intent)
1029   {
1030     case 0:
1031       return "Perceptual Intent";
1032
1033     case 1:
1034       return "Relative Intent";
1035
1036     case 2:
1037       return "Saturation Intent";
1038
1039     case 3:
1040       return "Absolute Intent";
1041
1042     default:
1043       return "Undefined Intent";
1044     }
1045 }
1046
1047 static const char *
1048 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1049 {
1050   switch (ping_colortype)
1051   {
1052     case 0:
1053       return "Grayscale";
1054
1055     case 2:
1056       return "Truecolor";
1057
1058     case 3:
1059       return "Indexed";
1060
1061     case 4:
1062       return "GrayAlpha";
1063
1064     case 6:
1065       return "RGBA";
1066
1067     default:
1068       return "UndefinedColorType";
1069     }
1070 }
1071
1072 #endif /* PNG_LIBPNG_VER > 10011 */
1073 #endif /* MAGICKCORE_PNG_DELEGATE */
1074 \f
1075 /*
1076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1077 %                                                                             %
1078 %                                                                             %
1079 %                                                                             %
1080 %   I s M N G                                                                 %
1081 %                                                                             %
1082 %                                                                             %
1083 %                                                                             %
1084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1085 %
1086 %  IsMNG() returns MagickTrue if the image format type, identified by the
1087 %  magick string, is MNG.
1088 %
1089 %  The format of the IsMNG method is:
1090 %
1091 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1092 %
1093 %  A description of each parameter follows:
1094 %
1095 %    o magick: compare image format pattern against these bytes.
1096 %
1097 %    o length: Specifies the length of the magick string.
1098 %
1099 %
1100 */
1101 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1102 {
1103   if (length < 8)
1104     return(MagickFalse);
1105
1106   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1107     return(MagickTrue);
1108
1109   return(MagickFalse);
1110 }
1111 \f
1112 /*
1113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1114 %                                                                             %
1115 %                                                                             %
1116 %                                                                             %
1117 %   I s J N G                                                                 %
1118 %                                                                             %
1119 %                                                                             %
1120 %                                                                             %
1121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1122 %
1123 %  IsJNG() returns MagickTrue if the image format type, identified by the
1124 %  magick string, is JNG.
1125 %
1126 %  The format of the IsJNG method is:
1127 %
1128 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1129 %
1130 %  A description of each parameter follows:
1131 %
1132 %    o magick: compare image format pattern against these bytes.
1133 %
1134 %    o length: Specifies the length of the magick string.
1135 %
1136 %
1137 */
1138 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1139 {
1140   if (length < 8)
1141     return(MagickFalse);
1142
1143   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1144     return(MagickTrue);
1145
1146   return(MagickFalse);
1147 }
1148 \f
1149 /*
1150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1151 %                                                                             %
1152 %                                                                             %
1153 %                                                                             %
1154 %   I s P N G                                                                 %
1155 %                                                                             %
1156 %                                                                             %
1157 %                                                                             %
1158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1159 %
1160 %  IsPNG() returns MagickTrue if the image format type, identified by the
1161 %  magick string, is PNG.
1162 %
1163 %  The format of the IsPNG method is:
1164 %
1165 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1166 %
1167 %  A description of each parameter follows:
1168 %
1169 %    o magick: compare image format pattern against these bytes.
1170 %
1171 %    o length: Specifies the length of the magick string.
1172 %
1173 */
1174 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1175 {
1176   if (length < 8)
1177     return(MagickFalse);
1178
1179   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1180     return(MagickTrue);
1181
1182   return(MagickFalse);
1183 }
1184 \f
1185 #if defined(MAGICKCORE_PNG_DELEGATE)
1186 #if defined(__cplusplus) || defined(c_plusplus)
1187 extern "C" {
1188 #endif
1189
1190 #if (PNG_LIBPNG_VER > 10011)
1191 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1192 {
1193   unsigned char
1194     buffer[4];
1195
1196   assert(image != (Image *) NULL);
1197   assert(image->signature == MagickCoreSignature);
1198   buffer[0]=(unsigned char) (value >> 24);
1199   buffer[1]=(unsigned char) (value >> 16);
1200   buffer[2]=(unsigned char) (value >> 8);
1201   buffer[3]=(unsigned char) value;
1202   return((size_t) WriteBlob(image,4,buffer));
1203 }
1204
1205 static void PNGLong(png_bytep p,png_uint_32 value)
1206 {
1207   *p++=(png_byte) ((value >> 24) & 0xff);
1208   *p++=(png_byte) ((value >> 16) & 0xff);
1209   *p++=(png_byte) ((value >> 8) & 0xff);
1210   *p++=(png_byte) (value & 0xff);
1211 }
1212
1213 static void PNGsLong(png_bytep p,png_int_32 value)
1214 {
1215   *p++=(png_byte) ((value >> 24) & 0xff);
1216   *p++=(png_byte) ((value >> 16) & 0xff);
1217   *p++=(png_byte) ((value >> 8) & 0xff);
1218   *p++=(png_byte) (value & 0xff);
1219 }
1220
1221 static void PNGShort(png_bytep p,png_uint_16 value)
1222 {
1223   *p++=(png_byte) ((value >> 8) & 0xff);
1224   *p++=(png_byte) (value & 0xff);
1225 }
1226
1227 static void PNGType(png_bytep p,const png_byte *type)
1228 {
1229   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1230 }
1231
1232 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1233    size_t length)
1234 {
1235   if (logging != MagickFalse)
1236     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1237       "  Writing %c%c%c%c chunk, length: %.20g",
1238       type[0],type[1],type[2],type[3],(double) length);
1239 }
1240 #endif /* PNG_LIBPNG_VER > 10011 */
1241
1242 #if defined(__cplusplus) || defined(c_plusplus)
1243 }
1244 #endif
1245
1246 #if PNG_LIBPNG_VER > 10011
1247 /*
1248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1249 %                                                                             %
1250 %                                                                             %
1251 %                                                                             %
1252 %   R e a d P N G I m a g e                                                   %
1253 %                                                                             %
1254 %                                                                             %
1255 %                                                                             %
1256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1257 %
1258 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1259 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1260 %  allocates the memory necessary for the new Image structure and returns a
1261 %  pointer to the new image or set of images.
1262 %
1263 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1264 %
1265 %  The format of the ReadPNGImage method is:
1266 %
1267 %     Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1268 %
1269 %  A description of each parameter follows:
1270 %
1271 %    o image_info: the image info.
1272 %
1273 %    o exception: return any errors or warnings in this structure.
1274 %
1275 %  To do, more or less in chronological order (as of version 5.5.2,
1276 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1277 %
1278 %    Get 16-bit cheap transparency working.
1279 %
1280 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1281 %
1282 %    Preserve all unknown and not-yet-handled known chunks found in input
1283 %    PNG file and copy them into output PNG files according to the PNG
1284 %    copying rules.
1285 %
1286 %    (At this point, PNG encoding should be in full MNG compliance)
1287 %
1288 %    Provide options for choice of background to use when the MNG BACK
1289 %    chunk is not present or is not mandatory (i.e., leave transparent,
1290 %    user specified, MNG BACK, PNG bKGD)
1291 %
1292 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1293 %    efficiently by linking in the duplicate frames.].
1294 %
1295 %    Decode and act on the MHDR simplicity profile (offer option to reject
1296 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1297 %
1298 %    Upgrade to full MNG without Delta-PNG.
1299 %
1300 %        o  BACK [done a while ago except for background image ID]
1301 %        o  MOVE [done 15 May 1999]
1302 %        o  CLIP [done 15 May 1999]
1303 %        o  DISC [done 19 May 1999]
1304 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1305 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1306 %        o  SHOW
1307 %        o  PAST
1308 %        o  BASI
1309 %        o  MNG-level tEXt/iTXt/zTXt
1310 %        o  pHYg
1311 %        o  pHYs
1312 %        o  sBIT
1313 %        o  bKGD
1314 %        o  iTXt (wait for libpng implementation).
1315 %
1316 %    Use the scene signature to discover when an identical scene is
1317 %    being reused, and just point to the original image->exception instead
1318 %    of storing another set of pixels.  This not specific to MNG
1319 %    but could be applied generally.
1320 %
1321 %    Upgrade to full MNG with Delta-PNG.
1322 %
1323 %    JNG tEXt/iTXt/zTXt
1324 %
1325 %    We will not attempt to read files containing the CgBI chunk.
1326 %    They are really Xcode files meant for display on the iPhone.
1327 %    These are not valid PNG files and it is impossible to recover
1328 %    the original PNG from files that have been converted to Xcode-PNG,
1329 %    since irretrievable loss of color data has occurred due to the
1330 %    use of premultiplied alpha.
1331 */
1332
1333 #if defined(__cplusplus) || defined(c_plusplus)
1334 extern "C" {
1335 #endif
1336
1337 /*
1338   This the function that does the actual reading of data.  It is
1339   the same as the one supplied in libpng, except that it receives the
1340   datastream from the ReadBlob() function instead of standard input.
1341 */
1342 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1343 {
1344   Image
1345     *image;
1346
1347   image=(Image *) png_get_io_ptr(png_ptr);
1348   if (length != 0)
1349     {
1350       png_size_t
1351         check;
1352
1353       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1354       if (check != length)
1355         {
1356           char
1357             msg[MagickPathExtent];
1358
1359           (void) FormatLocaleString(msg,MagickPathExtent,
1360             "Expected %.20g bytes; found %.20g bytes",(double) length,
1361             (double) check);
1362           png_warning(png_ptr,msg);
1363           png_error(png_ptr,"Read Exception");
1364         }
1365     }
1366 }
1367
1368 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1369     !defined(PNG_MNG_FEATURES_SUPPORTED)
1370 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1371  * older than libpng-1.0.3a, which was the first to allow the empty
1372  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1373  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1374  * encountered after an empty PLTE, so we have to look ahead for bKGD
1375  * chunks and remove them from the datastream that is passed to libpng,
1376  * and store their contents for later use.
1377  */
1378 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1379 {
1380   MngInfo
1381     *mng_info;
1382
1383   Image
1384     *image;
1385
1386   png_size_t
1387     check;
1388
1389   register ssize_t
1390     i;
1391
1392   i=0;
1393   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1394   image=(Image *) mng_info->image;
1395   while (mng_info->bytes_in_read_buffer && length)
1396   {
1397     data[i]=mng_info->read_buffer[i];
1398     mng_info->bytes_in_read_buffer--;
1399     length--;
1400     i++;
1401   }
1402   if (length != 0)
1403     {
1404       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1405
1406       if (check != length)
1407         png_error(png_ptr,"Read Exception");
1408
1409       if (length == 4)
1410         {
1411           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1412               (data[3] == 0))
1413             {
1414               check=(png_size_t) ReadBlob(image,(size_t) length,
1415                 (char *) mng_info->read_buffer);
1416               mng_info->read_buffer[4]=0;
1417               mng_info->bytes_in_read_buffer=4;
1418               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1419                 mng_info->found_empty_plte=MagickTrue;
1420               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1421                 {
1422                   mng_info->found_empty_plte=MagickFalse;
1423                   mng_info->have_saved_bkgd_index=MagickFalse;
1424                 }
1425             }
1426
1427           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1428               (data[3] == 1))
1429             {
1430               check=(png_size_t) ReadBlob(image,(size_t) length,
1431                 (char *) mng_info->read_buffer);
1432               mng_info->read_buffer[4]=0;
1433               mng_info->bytes_in_read_buffer=4;
1434               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1435                 if (mng_info->found_empty_plte)
1436                   {
1437                     /*
1438                       Skip the bKGD data byte and CRC.
1439                     */
1440                     check=(png_size_t)
1441                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1442                     check=(png_size_t) ReadBlob(image,(size_t) length,
1443                       (char *) mng_info->read_buffer);
1444                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1445                     mng_info->have_saved_bkgd_index=MagickTrue;
1446                     mng_info->bytes_in_read_buffer=0;
1447                   }
1448             }
1449         }
1450     }
1451 }
1452 #endif
1453
1454 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1455 {
1456   Image
1457     *image;
1458
1459   image=(Image *) png_get_io_ptr(png_ptr);
1460   if (length != 0)
1461     {
1462       png_size_t
1463         check;
1464
1465       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1466
1467       if (check != length)
1468         png_error(png_ptr,"WriteBlob Failed");
1469     }
1470 }
1471
1472 static void png_flush_data(png_structp png_ptr)
1473 {
1474   (void) png_ptr;
1475 }
1476
1477 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1478 static int PalettesAreEqual(Image *a,Image *b)
1479 {
1480   ssize_t
1481     i;
1482
1483   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1484     return((int) MagickFalse);
1485
1486   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1487     return((int) MagickFalse);
1488
1489   if (a->colors != b->colors)
1490     return((int) MagickFalse);
1491
1492   for (i=0; i < (ssize_t) a->colors; i++)
1493   {
1494     if ((a->colormap[i].red != b->colormap[i].red) ||
1495         (a->colormap[i].green != b->colormap[i].green) ||
1496         (a->colormap[i].blue != b->colormap[i].blue))
1497       return((int) MagickFalse);
1498   }
1499
1500   return((int) MagickTrue);
1501 }
1502 #endif
1503
1504 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1505 {
1506   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1507       mng_info->exists[i] && !mng_info->frozen[i])
1508     {
1509 #ifdef MNG_OBJECT_BUFFERS
1510       if (mng_info->ob[i] != (MngBuffer *) NULL)
1511         {
1512           if (mng_info->ob[i]->reference_count > 0)
1513             mng_info->ob[i]->reference_count--;
1514
1515           if (mng_info->ob[i]->reference_count == 0)
1516             {
1517               if (mng_info->ob[i]->image != (Image *) NULL)
1518                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1519
1520               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1521             }
1522         }
1523       mng_info->ob[i]=(MngBuffer *) NULL;
1524 #endif
1525       mng_info->exists[i]=MagickFalse;
1526       mng_info->invisible[i]=MagickFalse;
1527       mng_info->viewable[i]=MagickFalse;
1528       mng_info->frozen[i]=MagickFalse;
1529       mng_info->x_off[i]=0;
1530       mng_info->y_off[i]=0;
1531       mng_info->object_clip[i].left=0;
1532       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1533       mng_info->object_clip[i].top=0;
1534       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1535     }
1536 }
1537
1538 static MngInfo *MngInfoFreeStruct(MngInfo *mng_info)
1539 {
1540   register ssize_t
1541     i;
1542
1543   if (mng_info == (MngInfo *) NULL)
1544     return((MngInfo *) NULL);
1545
1546   for (i=1; i < MNG_MAX_OBJECTS; i++)
1547     MngInfoDiscardObject(mng_info,i);
1548
1549   mng_info->global_plte=(png_colorp)
1550     RelinquishMagickMemory(mng_info->global_plte);
1551
1552   return((MngInfo *) RelinquishMagickMemory(mng_info));
1553 }
1554
1555 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1556 {
1557   MngBox
1558     box;
1559
1560   box=box1;
1561   if (box.left < box2.left)
1562     box.left=box2.left;
1563
1564   if (box.top < box2.top)
1565     box.top=box2.top;
1566
1567   if (box.right > box2.right)
1568     box.right=box2.right;
1569
1570   if (box.bottom > box2.bottom)
1571     box.bottom=box2.bottom;
1572
1573   return box;
1574 }
1575
1576 static long mng_get_long(unsigned char *p)
1577 {
1578   return ((long) (((png_uint_32) p[0] << 24) | ((png_uint_32) p[1] << 16) |
1579     ((png_uint_32) p[2] << 8) | (png_uint_32) p[3]));
1580 }
1581
1582 static MngBox mng_read_box(MngBox previous_box,char delta_type,
1583   unsigned char *p)
1584 {
1585    MngBox
1586       box;
1587
1588   /*
1589     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1590   */
1591   box.left=mng_get_long(p);
1592   box.right=mng_get_long(&p[4]);
1593   box.top=mng_get_long(&p[8]);
1594   box.bottom=mng_get_long(&p[12]);
1595   if (delta_type != 0)
1596     {
1597       box.left+=previous_box.left;
1598       box.right+=previous_box.right;
1599       box.top+=previous_box.top;
1600       box.bottom+=previous_box.bottom;
1601     }
1602
1603   return(box);
1604 }
1605
1606 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1607   unsigned char *p)
1608 {
1609   MngPair
1610     pair;
1611
1612   /*
1613     Read two ssize_t's from CLON, MOVE or PAST chunk
1614   */
1615   pair.a=mng_get_long(p);
1616   pair.b=mng_get_long(&p[4]);
1617   if (delta_type != 0)
1618     {
1619       pair.a+=previous_pair.a;
1620       pair.b+=previous_pair.b;
1621     }
1622
1623   return(pair);
1624 }
1625
1626 typedef struct _PNGErrorInfo
1627 {
1628   Image
1629     *image;
1630
1631   ExceptionInfo
1632     *exception;
1633 } PNGErrorInfo;
1634
1635 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1636 {
1637   ExceptionInfo
1638     *exception;
1639
1640   Image
1641     *image;
1642
1643   PNGErrorInfo
1644     *error_info;
1645
1646   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1647   image=error_info->image;
1648   exception=error_info->exception;
1649
1650   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1651     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1652
1653   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1654     "`%s'",image->filename);
1655
1656 #if (PNG_LIBPNG_VER < 10500)
1657   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1658    * are building with libpng-1.4.x and can be ignored.
1659    */
1660   longjmp(ping->jmpbuf,1);
1661 #else
1662   png_longjmp(ping,1);
1663 #endif
1664 }
1665
1666 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1667 {
1668   ExceptionInfo
1669     *exception;
1670
1671   Image
1672     *image;
1673
1674   PNGErrorInfo
1675     *error_info;
1676
1677   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1678     png_error(ping, message);
1679
1680   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1681   image=error_info->image;
1682   exception=error_info->exception;
1683   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1684     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1685
1686   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1687     message,"`%s'",image->filename);
1688 }
1689
1690 #ifdef PNG_USER_MEM_SUPPORTED
1691 #if PNG_LIBPNG_VER >= 10400
1692 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1693 #else
1694 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1695 #endif
1696 {
1697   (void) png_ptr;
1698   return((png_voidp) AcquireMagickMemory((size_t) size));
1699 }
1700
1701 /*
1702   Free a pointer.  It is removed from the list at the same time.
1703 */
1704 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1705 {
1706   (void) png_ptr;
1707   ptr=RelinquishMagickMemory(ptr);
1708   return((png_free_ptr) NULL);
1709 }
1710 #endif
1711
1712 #if defined(__cplusplus) || defined(c_plusplus)
1713 }
1714 #endif
1715
1716 static int
1717 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1718    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1719 {
1720   register ssize_t
1721     i;
1722
1723   register unsigned char
1724     *dp;
1725
1726   register png_charp
1727     sp;
1728
1729   png_uint_32
1730     length,
1731     nibbles;
1732
1733   StringInfo
1734     *profile;
1735
1736   const unsigned char
1737     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1738                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1739                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1740                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1741                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1742                  13,14,15};
1743
1744   sp=text[ii].text+1;
1745   /* look for newline */
1746   while (*sp != '\n')
1747      sp++;
1748
1749   /* look for length */
1750   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1751      sp++;
1752
1753   length=(png_uint_32) StringToLong(sp);
1754
1755   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1756        "      length: %lu",(unsigned long) length);
1757
1758   while (*sp != ' ' && *sp != '\n')
1759      sp++;
1760
1761   /* allocate space */
1762   if (length == 0)
1763   {
1764     png_warning(ping,"invalid profile length");
1765     return(MagickFalse);
1766   }
1767
1768   profile=BlobToStringInfo((const void *) NULL,length);
1769
1770   if (profile == (StringInfo *) NULL)
1771   {
1772     png_warning(ping, "unable to copy profile");
1773     return(MagickFalse);
1774   }
1775
1776   /* copy profile, skipping white space and column 1 "=" signs */
1777   dp=GetStringInfoDatum(profile);
1778   nibbles=length*2;
1779
1780   for (i=0; i < (ssize_t) nibbles; i++)
1781   {
1782     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1783     {
1784       if (*sp == '\0')
1785         {
1786           png_warning(ping, "ran out of profile data");
1787           profile=DestroyStringInfo(profile);
1788           return(MagickFalse);
1789         }
1790       sp++;
1791     }
1792
1793     if (i%2 == 0)
1794       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1795
1796     else
1797       (*dp++)+=unhex[(int) *sp++];
1798   }
1799   /*
1800     We have already read "Raw profile type.
1801   */
1802   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1803   profile=DestroyStringInfo(profile);
1804
1805   if (image_info->verbose)
1806     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1807
1808   return MagickTrue;
1809 }
1810
1811 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1812
1813 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1814 {
1815   Image
1816     *image;
1817
1818
1819   /* The unknown chunk structure contains the chunk data:
1820      png_byte name[5];
1821      png_byte *data;
1822      png_size_t size;
1823
1824      Note that libpng has already taken care of the CRC handling.
1825
1826      Returns one of the following:
1827          return(-n);  chunk had an error
1828          return(0);  did not recognize
1829          return(n);  success
1830   */
1831
1832   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1833      "    read_user_chunk: found %c%c%c%c chunk",
1834        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1835
1836   if (chunk->name[0]  == 101 &&
1837       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
1838       chunk->name[2] ==   73 &&
1839       chunk-> name[3] == 102)
1840     {
1841       /* process eXIf or exIf chunk */
1842
1843       PNGErrorInfo
1844         *error_info;
1845
1846       StringInfo
1847         *profile;
1848
1849       unsigned char
1850         *p;
1851
1852       png_byte
1853         *s;
1854
1855       size_t
1856         i;
1857
1858       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1859         " recognized eXIf chunk");
1860
1861       image=(Image *) png_get_user_chunk_ptr(ping);
1862
1863       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1864
1865       profile=BlobToStringInfo((const void *) NULL,chunk->size+6);
1866
1867       if (profile == (StringInfo *) NULL)
1868         {
1869           (void) ThrowMagickException(error_info->exception,GetMagickModule(),
1870             ResourceLimitError,"MemoryAllocationFailed","`%s'",
1871             image->filename);
1872           return(-1);
1873         }
1874       p=GetStringInfoDatum(profile);
1875
1876       if (*p != 'E')
1877         {
1878           /* Initialize profile with "Exif\0\0" if it is not
1879              already present by accident
1880           */
1881           *p++ ='E';
1882           *p++ ='x';
1883           *p++ ='i';
1884           *p++ ='f';
1885           *p++ ='\0';
1886           *p++ ='\0';
1887         }
1888       else
1889         {
1890           if (p[1] != 'x' || p[2] != 'i' || p[3] != 'f' ||
1891               p[4] != '\0' || p[5] != '\0')
1892             {
1893               /* Chunk is malformed */
1894               profile=DestroyStringInfo(profile);
1895               return(-1);
1896             }
1897          }
1898
1899       /* copy chunk->data to profile */
1900       s=chunk->data;
1901       for (i=0; i<chunk->size; i++)
1902         *p++ = *s++;
1903
1904       error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1905       (void) SetImageProfile(image,"exif",profile,
1906         error_info->exception);
1907
1908       profile=DestroyStringInfo(profile);
1909
1910       return(1);
1911     }
1912
1913   /* vpAg (deprecated, replaced by caNv) */
1914   if (chunk->name[0] == 118 &&
1915       chunk->name[1] == 112 &&
1916       chunk->name[2] ==  65 &&
1917       chunk->name[3] == 103)
1918     {
1919       /* recognized vpAg */
1920
1921       if (chunk->size != 9)
1922         return(-1); /* Error return */
1923
1924       if (chunk->data[8] != 0)
1925         return(0);  /* ImageMagick requires pixel units */
1926
1927       image=(Image *) png_get_user_chunk_ptr(ping);
1928
1929       image->page.width=(size_t)mng_get_long(chunk->data);
1930       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
1931
1932       return(1);
1933     }
1934
1935   /* caNv */
1936   if (chunk->name[0] ==  99 &&
1937       chunk->name[1] ==  97 &&
1938       chunk->name[2] ==  78 &&
1939       chunk->name[3] == 118)
1940     {
1941       /* recognized caNv */
1942
1943       if (chunk->size != 16)
1944         return(-1); /* Error return */
1945
1946       image=(Image *) png_get_user_chunk_ptr(ping);
1947
1948       image->page.width=(size_t)mng_get_long(chunk->data);
1949       image->page.height=(size_t)mng_get_long(&chunk->data[4]);
1950       image->page.x=(size_t)mng_get_long(&chunk->data[8]);
1951       image->page.y=(size_t)mng_get_long(&chunk->data[12]);
1952
1953       return(1);
1954     }
1955
1956   return(0); /* Did not recognize */
1957 }
1958 #endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */
1959
1960 #if defined(PNG_tIME_SUPPORTED)
1961 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1962   ExceptionInfo *exception)
1963 {
1964   png_timep
1965     time;
1966
1967   if (png_get_tIME(ping,info,&time))
1968     {
1969       char
1970         timestamp[21];
1971
1972       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1973         time->year,time->month,time->day,time->hour,time->minute,time->second);
1974       SetImageProperty(image,"png:tIME",timestamp,exception);
1975     }
1976 }
1977 #endif
1978
1979 /*
1980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1981 %                                                                             %
1982 %                                                                             %
1983 %                                                                             %
1984 %   R e a d O n e P N G I m a g e                                             %
1985 %                                                                             %
1986 %                                                                             %
1987 %                                                                             %
1988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1989 %
1990 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1991 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
1992 %  necessary for the new Image structure and returns a pointer to the new
1993 %  image.
1994 %
1995 %  The format of the ReadOnePNGImage method is:
1996 %
1997 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1998 %         ExceptionInfo *exception)
1999 %
2000 %  A description of each parameter follows:
2001 %
2002 %    o mng_info: Specifies a pointer to a MngInfo structure.
2003 %
2004 %    o image_info: the image info.
2005 %
2006 %    o exception: return any errors or warnings in this structure.
2007 %
2008 */
2009 static Image *ReadOnePNGImage(MngInfo *mng_info,
2010     const ImageInfo *image_info, ExceptionInfo *exception)
2011 {
2012   /* Read one PNG image */
2013
2014   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
2015
2016   Image
2017     *image;
2018
2019   char
2020     im_vers[32],
2021     libpng_runv[32],
2022     libpng_vers[32],
2023     zlib_runv[32],
2024     zlib_vers[32];
2025
2026   int
2027     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
2028     num_raw_profiles,
2029     num_text,
2030     num_text_total,
2031     num_passes,
2032     number_colors,
2033     pass,
2034     ping_bit_depth,
2035     ping_color_type,
2036     ping_file_depth,
2037     ping_interlace_method,
2038     ping_compression_method,
2039     ping_filter_method,
2040     ping_num_trans,
2041     unit_type;
2042
2043   double
2044     file_gamma;
2045
2046   MagickBooleanType
2047     logging,
2048     ping_found_cHRM,
2049     ping_found_gAMA,
2050     ping_found_iCCP,
2051     ping_found_sRGB,
2052     ping_found_sRGB_cHRM,
2053     ping_preserve_iCCP,
2054     status;
2055
2056   MemoryInfo
2057     *volatile pixel_info;
2058
2059   PixelInfo
2060     transparent_color;
2061
2062   PNGErrorInfo
2063     error_info;
2064
2065   png_bytep
2066      ping_trans_alpha;
2067
2068   png_color_16p
2069      ping_background,
2070      ping_trans_color;
2071
2072   png_info
2073     *end_info,
2074     *ping_info;
2075
2076   png_struct
2077     *ping;
2078
2079   png_textp
2080     text;
2081
2082   png_uint_32
2083     ping_height,
2084     ping_width,
2085     x_resolution,
2086     y_resolution;
2087
2088   QuantumInfo
2089     *volatile quantum_info;
2090
2091   Quantum
2092     *volatile quantum_scanline;
2093
2094   ssize_t
2095     ping_rowbytes,
2096     y;
2097
2098   register unsigned char
2099     *p;
2100
2101   register ssize_t
2102     i,
2103     x;
2104
2105   register Quantum
2106     *q;
2107
2108   size_t
2109     length,
2110     row_offset;
2111
2112   ssize_t
2113     j;
2114
2115   unsigned char
2116     *ping_pixels;
2117
2118 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2119   png_byte unused_chunks[]=
2120   {
2121     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2122     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2123     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2124     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2125     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2126 #if !defined(PNG_tIME_SUPPORTED)
2127     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2128 #endif
2129 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2130                           /* ignore the APNG chunks */
2131      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2132     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2133     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2134 #endif
2135   };
2136 #endif
2137
2138   /* Define these outside of the following "if logging()" block so they will
2139    * show in debuggers.
2140    */
2141   *im_vers='\0';
2142   (void) ConcatenateMagickString(im_vers,
2143          MagickLibVersionText,32);
2144   (void) ConcatenateMagickString(im_vers,
2145          MagickLibAddendum,32);
2146
2147   *libpng_vers='\0';
2148   (void) ConcatenateMagickString(libpng_vers,
2149          PNG_LIBPNG_VER_STRING,32);
2150   *libpng_runv='\0';
2151   (void) ConcatenateMagickString(libpng_runv,
2152          png_get_libpng_ver(NULL),32);
2153
2154   *zlib_vers='\0';
2155   (void) ConcatenateMagickString(zlib_vers,
2156          ZLIB_VERSION,32);
2157   *zlib_runv='\0';
2158   (void) ConcatenateMagickString(zlib_runv,
2159          zlib_version,32);
2160
2161   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2162        "  Enter ReadOnePNGImage()\n"
2163        "    IM version     = %s\n"
2164        "    Libpng version = %s",
2165        im_vers, libpng_vers);
2166
2167   if (logging != MagickFalse)
2168   {
2169     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2170     {
2171    (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2172         libpng_runv);
2173     }
2174     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
2175         zlib_vers);
2176     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2177     {
2178     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2179         zlib_runv);
2180     }
2181   }
2182
2183 #if (PNG_LIBPNG_VER < 10200)
2184   if (image_info->verbose)
2185     printf("Your PNG library (libpng-%s) is rather old.\n",
2186        PNG_LIBPNG_VER_STRING);
2187 #endif
2188
2189 #if (PNG_LIBPNG_VER >= 10400)
2190 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2191   if (image_info->verbose)
2192     {
2193       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2194            PNG_LIBPNG_VER_STRING);
2195       printf("Please update it.\n");
2196     }
2197 #  endif
2198 #endif
2199
2200
2201   quantum_info = (QuantumInfo *) NULL;
2202   image=mng_info->image;
2203
2204   if (logging != MagickFalse)
2205   {
2206     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2207        "    Before reading:\n"
2208        "      image->alpha_trait=%d\n"
2209        "      image->rendering_intent=%d\n"
2210        "      image->colorspace=%d\n"
2211        "      image->gamma=%f",
2212        (int) image->alpha_trait, (int) image->rendering_intent,
2213        (int) image->colorspace, image->gamma);
2214   }
2215   intent=
2216     Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2217
2218   /* Set to an out-of-range color unless tRNS chunk is present */
2219   transparent_color.red=65537;
2220   transparent_color.green=65537;
2221   transparent_color.blue=65537;
2222   transparent_color.alpha=65537;
2223
2224   number_colors=0;
2225   num_text = 0;
2226   num_text_total = 0;
2227   num_raw_profiles = 0;
2228
2229   ping_found_cHRM = MagickFalse;
2230   ping_found_gAMA = MagickFalse;
2231   ping_found_iCCP = MagickFalse;
2232   ping_found_sRGB = MagickFalse;
2233   ping_found_sRGB_cHRM = MagickFalse;
2234   ping_preserve_iCCP = MagickFalse;
2235
2236
2237   /*
2238     Allocate the PNG structures
2239   */
2240 #ifdef PNG_USER_MEM_SUPPORTED
2241  error_info.image=image;
2242  error_info.exception=exception;
2243  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2244    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2245    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2246 #else
2247   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2248     MagickPNGErrorHandler,MagickPNGWarningHandler);
2249 #endif
2250   if (ping == (png_struct *) NULL)
2251     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2252
2253   ping_info=png_create_info_struct(ping);
2254
2255   if (ping_info == (png_info *) NULL)
2256     {
2257       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2258       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2259     }
2260
2261   end_info=png_create_info_struct(ping);
2262
2263   if (end_info == (png_info *) NULL)
2264     {
2265       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2266       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2267     }
2268
2269   pixel_info=(MemoryInfo *) NULL;
2270   quantum_scanline = (Quantum *) NULL;
2271   quantum_info = (QuantumInfo *) NULL;
2272
2273   if (setjmp(png_jmpbuf(ping)))
2274     {
2275       /*
2276         PNG image is corrupt.
2277       */
2278       png_destroy_read_struct(&ping,&ping_info,&end_info);
2279
2280       if (pixel_info != (MemoryInfo *) NULL)
2281         pixel_info=RelinquishVirtualMemory(pixel_info);
2282
2283       if (quantum_info != (QuantumInfo *) NULL)
2284         quantum_info=DestroyQuantumInfo(quantum_info);
2285
2286       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
2287
2288 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2289       UnlockSemaphoreInfo(ping_semaphore);
2290 #endif
2291
2292       if (logging != MagickFalse)
2293         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2294           "  exit ReadOnePNGImage() with error.");
2295
2296       return(GetFirstImageInList(image));
2297     }
2298
2299   /* {  For navigation to end of SETJMP-protected block.  Within this
2300    *    block, use png_error() instead of Throwing an Exception, to ensure
2301    *    that libpng is able to clean up, and that the semaphore is unlocked.
2302    */
2303
2304 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2305   LockSemaphoreInfo(ping_semaphore);
2306 #endif
2307
2308 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2309   /* Allow benign errors */
2310   png_set_benign_errors(ping, 1);
2311 #endif
2312
2313 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2314   /* Reject images with too many rows or columns */
2315   png_set_user_limits(ping,
2316     (png_uint_32) MagickMin(0x7fffffffL,
2317         GetMagickResourceLimit(WidthResource)),
2318     (png_uint_32) MagickMin(0x7fffffffL,
2319         GetMagickResourceLimit(HeightResource)));
2320 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2321
2322   /*
2323     Prepare PNG for reading.
2324   */
2325
2326   mng_info->image_found++;
2327   png_set_sig_bytes(ping,8);
2328
2329   if (LocaleCompare(image_info->magick,"MNG") == 0)
2330     {
2331 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2332       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2333       png_set_read_fn(ping,image,png_get_data);
2334 #else
2335 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2336       png_permit_empty_plte(ping,MagickTrue);
2337       png_set_read_fn(ping,image,png_get_data);
2338 #else
2339       mng_info->image=image;
2340       mng_info->bytes_in_read_buffer=0;
2341       mng_info->found_empty_plte=MagickFalse;
2342       mng_info->have_saved_bkgd_index=MagickFalse;
2343       png_set_read_fn(ping,mng_info,mng_get_data);
2344 #endif
2345 #endif
2346     }
2347
2348   else
2349     png_set_read_fn(ping,image,png_get_data);
2350
2351   {
2352     const char
2353       *value;
2354
2355     value=GetImageOption(image_info,"profile:skip");
2356
2357     if (IsOptionMember("ICC",value) == MagickFalse)
2358     {
2359
2360        value=GetImageOption(image_info,"png:preserve-iCCP");
2361
2362        if (value == NULL)
2363           value=GetImageArtifact(image,"png:preserve-iCCP");
2364
2365        if (value != NULL)
2366           ping_preserve_iCCP=MagickTrue;
2367
2368 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2369        /* Don't let libpng check for ICC/sRGB profile because we're going
2370         * to do that anyway.  This feature was added at libpng-1.6.12.
2371         * If logging, go ahead and check and issue a warning as appropriate.
2372         */
2373        if (logging == MagickFalse)
2374           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2375 #endif
2376     }
2377 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2378     else
2379     {
2380        png_set_keep_unknown_chunks(ping, 1, (png_bytep) mng_iCCP, 1);
2381     }
2382 #endif
2383   }
2384 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2385   /* Ignore unused chunks and all unknown chunks except for caNv and vpAg */
2386 # if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2387   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2388 # else
2389   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2390 # endif
2391   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
2392   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_vpAg, 1);
2393   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2394      (int)sizeof(unused_chunks)/5);
2395   /* Callback for other unknown chunks */
2396   png_set_read_user_chunk_fn(ping, image, read_user_chunk_callback);
2397 #endif
2398
2399 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2400 #  if (PNG_LIBPNG_VER >= 10400)
2401     /* Limit the size of the chunk storage cache used for sPLT, text,
2402      * and unknown chunks.
2403      */
2404     png_set_chunk_cache_max(ping, 32767);
2405 #  endif
2406 #endif
2407
2408 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2409     /* Disable new libpng-1.5.10 feature */
2410     png_set_check_for_invalid_index (ping, 0);
2411 #endif
2412
2413 #if (PNG_LIBPNG_VER < 10400)
2414 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2415    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2416   /* Disable thread-unsafe features of pnggccrd */
2417   if (png_access_version_number() >= 10200)
2418   {
2419     png_uint_32 mmx_disable_mask=0;
2420     png_uint_32 asm_flags;
2421
2422     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2423                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2424                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2425                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2426     asm_flags=png_get_asm_flags(ping);
2427     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2428   }
2429 #  endif
2430 #endif
2431
2432   png_read_info(ping,ping_info);
2433
2434   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2435                &ping_bit_depth,&ping_color_type,
2436                &ping_interlace_method,&ping_compression_method,
2437                &ping_filter_method);
2438
2439   ping_file_depth = ping_bit_depth;
2440
2441   /* Swap bytes if requested */
2442   if (ping_file_depth == 16)
2443   {
2444      const char
2445        *value;
2446
2447      value=GetImageOption(image_info,"png:swap-bytes");
2448
2449      if (value == NULL)
2450         value=GetImageArtifact(image,"png:swap-bytes");
2451
2452      if (value != NULL)
2453         png_set_swap(ping);
2454   }
2455
2456   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2457   {
2458       char
2459         msg[MagickPathExtent];
2460
2461       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2462          (int) ping_color_type);
2463       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2464
2465       (void) FormatLocaleString(msg,MagickPathExtent,"%d",
2466          (int) ping_bit_depth);
2467       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2468   }
2469
2470   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2471                       &ping_trans_color);
2472
2473   (void) png_get_bKGD(ping, ping_info, &ping_background);
2474
2475   if (ping_bit_depth < 8)
2476     {
2477        png_set_packing(ping);
2478        ping_bit_depth = 8;
2479     }
2480
2481   image->depth=ping_bit_depth;
2482   image->depth=GetImageQuantumDepth(image,MagickFalse);
2483   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2484
2485   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2486       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2487     {
2488       image->rendering_intent=UndefinedIntent;
2489       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2490       (void) ResetMagickMemory(&image->chromaticity,0,
2491         sizeof(image->chromaticity));
2492     }
2493
2494   if (logging != MagickFalse)
2495     {
2496       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2497         "    PNG width: %.20g, height: %.20g\n"
2498         "    PNG color_type: %d, bit_depth: %d\n"
2499         "    PNG compression_method: %d\n"
2500         "    PNG interlace_method: %d, filter_method: %d",
2501         (double) ping_width, (double) ping_height,
2502         ping_color_type, ping_bit_depth,
2503         ping_compression_method,
2504         ping_interlace_method,ping_filter_method);
2505
2506     }
2507
2508   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2509     {
2510       ping_found_iCCP=MagickTrue;
2511       if (logging != MagickFalse)
2512         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2513           "    Found PNG iCCP chunk.");
2514     }
2515
2516   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2517     {
2518       ping_found_gAMA=MagickTrue;
2519       if (logging != MagickFalse)
2520         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2521           "    Found PNG gAMA chunk.");
2522     }
2523
2524   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2525     {
2526       ping_found_cHRM=MagickTrue;
2527       if (logging != MagickFalse)
2528         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2529           "    Found PNG cHRM chunk.");
2530     }
2531
2532   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2533       PNG_INFO_sRGB))
2534     {
2535       ping_found_sRGB=MagickTrue;
2536       if (logging != MagickFalse)
2537         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2538           "    Found PNG sRGB chunk.");
2539     }
2540
2541 #ifdef PNG_READ_iCCP_SUPPORTED
2542     if (ping_found_iCCP !=MagickTrue &&
2543       ping_found_sRGB != MagickTrue &&
2544       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2545     {
2546       ping_found_iCCP=MagickTrue;
2547       if (logging != MagickFalse)
2548         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2549           "    Found PNG iCCP chunk.");
2550     }
2551
2552   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2553     {
2554       int
2555         compression;
2556
2557 #if (PNG_LIBPNG_VER < 10500)
2558       png_charp
2559         info;
2560 #else
2561       png_bytep
2562         info;
2563 #endif
2564
2565       png_charp
2566         name;
2567
2568       png_uint_32
2569         profile_length;
2570
2571       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2572         &profile_length);
2573
2574       if (profile_length != 0)
2575         {
2576           StringInfo
2577             *profile;
2578
2579           if (logging != MagickFalse)
2580             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2581               "    Reading PNG iCCP chunk.");
2582
2583           profile=BlobToStringInfo(info,profile_length);
2584
2585           if (profile == (StringInfo *) NULL)
2586           {
2587             png_warning(ping, "ICC profile is NULL");
2588             profile=DestroyStringInfo(profile);
2589           }
2590           else
2591           {
2592             if (ping_preserve_iCCP == MagickFalse)
2593             {
2594                  int
2595                    icheck,
2596                    got_crc=0;
2597
2598
2599                  png_uint_32
2600                    length,
2601                    profile_crc=0;
2602
2603                  unsigned char
2604                    *data;
2605
2606                  length=(png_uint_32) GetStringInfoLength(profile);
2607
2608                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2609                  {
2610                    if (length == sRGB_info[icheck].len)
2611                    {
2612                      if (got_crc == 0)
2613                      {
2614                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2615                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2616                          (unsigned long) length);
2617
2618                        data=GetStringInfoDatum(profile);
2619                        profile_crc=crc32(0,data,length);
2620
2621                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2622                            "      with crc=%8x",(unsigned int) profile_crc);
2623                        got_crc++;
2624                      }
2625
2626                      if (profile_crc == sRGB_info[icheck].crc)
2627                      {
2628                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2629                             "      It is sRGB with rendering intent = %s",
2630                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2631                              sRGB_info[icheck].intent));
2632                         if (image->rendering_intent==UndefinedIntent)
2633                         {
2634                           image->rendering_intent=
2635                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2636                              sRGB_info[icheck].intent);
2637                         }
2638                         break;
2639                      }
2640                    }
2641                  }
2642                  if (sRGB_info[icheck].len == 0)
2643                  {
2644                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2645                         "    Got %lu-byte ICC profile not recognized as sRGB",
2646                         (unsigned long) length);
2647                     (void) SetImageProfile(image,"icc",profile,exception);
2648                  }
2649             }
2650             else /* Preserve-iCCP */
2651             {
2652                     (void) SetImageProfile(image,"icc",profile,exception);
2653             }
2654
2655             profile=DestroyStringInfo(profile);
2656           }
2657       }
2658     }
2659 #endif
2660
2661 #if defined(PNG_READ_sRGB_SUPPORTED)
2662   {
2663     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2664         PNG_INFO_sRGB))
2665     {
2666       if (png_get_sRGB(ping,ping_info,&intent))
2667       {
2668         if (image->rendering_intent == UndefinedIntent)
2669           image->rendering_intent=
2670              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2671
2672         if (logging != MagickFalse)
2673           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2674             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2675       }
2676     }
2677
2678     else if (mng_info->have_global_srgb)
2679       {
2680         if (image->rendering_intent == UndefinedIntent)
2681           image->rendering_intent=
2682             Magick_RenderingIntent_from_PNG_RenderingIntent
2683             (mng_info->global_srgb_intent);
2684       }
2685   }
2686 #endif
2687
2688
2689   {
2690      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2691        if (mng_info->have_global_gama)
2692          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2693
2694      if (png_get_gAMA(ping,ping_info,&file_gamma))
2695        {
2696          image->gamma=(float) file_gamma;
2697          if (logging != MagickFalse)
2698            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2699              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2700        }
2701   }
2702
2703   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2704     {
2705       if (mng_info->have_global_chrm != MagickFalse)
2706         {
2707           (void) png_set_cHRM(ping,ping_info,
2708             mng_info->global_chrm.white_point.x,
2709             mng_info->global_chrm.white_point.y,
2710             mng_info->global_chrm.red_primary.x,
2711             mng_info->global_chrm.red_primary.y,
2712             mng_info->global_chrm.green_primary.x,
2713             mng_info->global_chrm.green_primary.y,
2714             mng_info->global_chrm.blue_primary.x,
2715             mng_info->global_chrm.blue_primary.y);
2716         }
2717     }
2718
2719   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2720     {
2721       (void) png_get_cHRM(ping,ping_info,
2722         &image->chromaticity.white_point.x,
2723         &image->chromaticity.white_point.y,
2724         &image->chromaticity.red_primary.x,
2725         &image->chromaticity.red_primary.y,
2726         &image->chromaticity.green_primary.x,
2727         &image->chromaticity.green_primary.y,
2728         &image->chromaticity.blue_primary.x,
2729         &image->chromaticity.blue_primary.y);
2730
2731        ping_found_cHRM=MagickTrue;
2732
2733        if (image->chromaticity.red_primary.x>0.6399f &&
2734            image->chromaticity.red_primary.x<0.6401f &&
2735            image->chromaticity.red_primary.y>0.3299f &&
2736            image->chromaticity.red_primary.y<0.3301f &&
2737            image->chromaticity.green_primary.x>0.2999f &&
2738            image->chromaticity.green_primary.x<0.3001f &&
2739            image->chromaticity.green_primary.y>0.5999f &&
2740            image->chromaticity.green_primary.y<0.6001f &&
2741            image->chromaticity.blue_primary.x>0.1499f &&
2742            image->chromaticity.blue_primary.x<0.1501f &&
2743            image->chromaticity.blue_primary.y>0.0599f &&
2744            image->chromaticity.blue_primary.y<0.0601f &&
2745            image->chromaticity.white_point.x>0.3126f &&
2746            image->chromaticity.white_point.x<0.3128f &&
2747            image->chromaticity.white_point.y>0.3289f &&
2748            image->chromaticity.white_point.y<0.3291f)
2749           ping_found_sRGB_cHRM=MagickTrue;
2750     }
2751
2752   if (image->rendering_intent != UndefinedIntent)
2753     {
2754       if (ping_found_sRGB != MagickTrue &&
2755           (ping_found_gAMA != MagickTrue ||
2756           (image->gamma > .45 && image->gamma < .46)) &&
2757           (ping_found_cHRM != MagickTrue ||
2758           ping_found_sRGB_cHRM != MagickFalse) &&
2759           ping_found_iCCP != MagickTrue)
2760       {
2761          png_set_sRGB(ping,ping_info,
2762             Magick_RenderingIntent_to_PNG_RenderingIntent
2763             (image->rendering_intent));
2764          file_gamma=1.000f/2.200f;
2765          ping_found_sRGB=MagickTrue;
2766          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2767            "    Setting sRGB as if in input");
2768       }
2769     }
2770
2771 #if defined(PNG_oFFs_SUPPORTED)
2772   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2773     {
2774       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2775       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2776
2777       if (logging != MagickFalse)
2778         if (image->page.x || image->page.y)
2779           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2780             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2781             image->page.x,(double) image->page.y);
2782     }
2783 #endif
2784 #if defined(PNG_pHYs_SUPPORTED)
2785   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2786     {
2787       if (mng_info->have_global_phys)
2788         {
2789           png_set_pHYs(ping,ping_info,
2790                        mng_info->global_x_pixels_per_unit,
2791                        mng_info->global_y_pixels_per_unit,
2792                        mng_info->global_phys_unit_type);
2793         }
2794     }
2795
2796   x_resolution=0;
2797   y_resolution=0;
2798   unit_type=0;
2799   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2800     {
2801       /*
2802         Set image resolution.
2803       */
2804       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2805         &unit_type);
2806       image->resolution.x=(double) x_resolution;
2807       image->resolution.y=(double) y_resolution;
2808
2809       if (unit_type == PNG_RESOLUTION_METER)
2810         {
2811           image->units=PixelsPerCentimeterResolution;
2812           image->resolution.x=(double) x_resolution/100.0;
2813           image->resolution.y=(double) y_resolution/100.0;
2814         }
2815
2816       if (logging != MagickFalse)
2817         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2818           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2819           (double) x_resolution,(double) y_resolution,unit_type);
2820     }
2821 #endif
2822
2823   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2824     {
2825       png_colorp
2826         palette;
2827
2828       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2829
2830       if ((number_colors == 0) &&
2831           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2832         {
2833           if (mng_info->global_plte_length)
2834             {
2835               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2836                 (int) mng_info->global_plte_length);
2837
2838               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2839               {
2840                 if (mng_info->global_trns_length)
2841                   {
2842                     png_warning(ping,
2843                       "global tRNS has more entries than global PLTE");
2844                   }
2845                 else
2846                   {
2847                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2848                        (int) mng_info->global_trns_length,NULL);
2849                   }
2850                }
2851 #ifdef PNG_READ_bKGD_SUPPORTED
2852               if (
2853 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2854                    mng_info->have_saved_bkgd_index ||
2855 #endif
2856                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2857                     {
2858                       png_color_16
2859                          background;
2860
2861 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2862                       if (mng_info->have_saved_bkgd_index)
2863                         background.index=mng_info->saved_bkgd_index;
2864 #endif
2865                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2866                         background.index=ping_background->index;
2867
2868                       background.red=(png_uint_16)
2869                         mng_info->global_plte[background.index].red;
2870
2871                       background.green=(png_uint_16)
2872                         mng_info->global_plte[background.index].green;
2873
2874                       background.blue=(png_uint_16)
2875                         mng_info->global_plte[background.index].blue;
2876
2877                       background.gray=(png_uint_16)
2878                         mng_info->global_plte[background.index].green;
2879
2880                       png_set_bKGD(ping,ping_info,&background);
2881                     }
2882 #endif
2883                 }
2884               else
2885                 png_error(ping,"No global PLTE in file");
2886             }
2887         }
2888
2889 #ifdef PNG_READ_bKGD_SUPPORTED
2890   if (mng_info->have_global_bkgd &&
2891           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2892       image->background_color=mng_info->mng_global_bkgd;
2893
2894   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2895     {
2896       unsigned int
2897         bkgd_scale;
2898
2899       /* Set image background color.
2900        * Scale background components to 16-bit, then scale
2901        * to quantum depth
2902        */
2903
2904         bkgd_scale = 1;
2905
2906         if (ping_file_depth == 1)
2907            bkgd_scale = 255;
2908
2909         else if (ping_file_depth == 2)
2910            bkgd_scale = 85;
2911
2912         else if (ping_file_depth == 4)
2913            bkgd_scale = 17;
2914
2915         if (ping_file_depth <= 8)
2916            bkgd_scale *= 257;
2917
2918         ping_background->red *= bkgd_scale;
2919         ping_background->green *= bkgd_scale;
2920         ping_background->blue *= bkgd_scale;
2921
2922         if (logging != MagickFalse)
2923           {
2924             if (logging != MagickFalse)
2925               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2926                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d)\n"
2927                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d)",
2928                  ping_background->red,ping_background->green,
2929                  ping_background->blue,
2930                  bkgd_scale,ping_background->red,
2931                  ping_background->green,ping_background->blue);
2932           }
2933
2934         image->background_color.red=
2935             ScaleShortToQuantum(ping_background->red);
2936
2937         image->background_color.green=
2938             ScaleShortToQuantum(ping_background->green);
2939
2940         image->background_color.blue=
2941           ScaleShortToQuantum(ping_background->blue);
2942
2943         image->background_color.alpha=OpaqueAlpha;
2944
2945         if (logging != MagickFalse)
2946           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2947             "    image->background_color=(%.20g,%.20g,%.20g).",
2948             (double) image->background_color.red,
2949             (double) image->background_color.green,
2950             (double) image->background_color.blue);
2951     }
2952 #endif /* PNG_READ_bKGD_SUPPORTED */
2953
2954   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2955     {
2956       /*
2957         Image has a tRNS chunk.
2958       */
2959       int
2960         max_sample;
2961
2962       size_t
2963         one = 1;
2964
2965       if (logging != MagickFalse)
2966         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2967           "    Reading PNG tRNS chunk.");
2968
2969       max_sample = (int) ((one << ping_file_depth) - 1);
2970
2971       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2972           (int)ping_trans_color->gray > max_sample) ||
2973           (ping_color_type == PNG_COLOR_TYPE_RGB &&
2974           ((int)ping_trans_color->red > max_sample ||
2975           (int)ping_trans_color->green > max_sample ||
2976           (int)ping_trans_color->blue > max_sample)))
2977         {
2978           if (logging != MagickFalse)
2979             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2980               "    Ignoring PNG tRNS chunk with out-of-range sample.");
2981           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2982           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2983           image->alpha_trait=UndefinedPixelTrait;
2984         }
2985       else
2986         {
2987           int
2988             scale_to_short;
2989
2990           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2991
2992           /* Scale transparent_color to short */
2993           transparent_color.red= scale_to_short*ping_trans_color->red;
2994           transparent_color.green= scale_to_short*ping_trans_color->green;
2995           transparent_color.blue= scale_to_short*ping_trans_color->blue;
2996           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2997
2998           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2999             {
3000               if (logging != MagickFalse)
3001               {
3002                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3003                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
3004                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
3005
3006               }
3007               transparent_color.red=transparent_color.alpha;
3008               transparent_color.green=transparent_color.alpha;
3009               transparent_color.blue=transparent_color.alpha;
3010             }
3011         }
3012     }
3013 #if defined(PNG_READ_sBIT_SUPPORTED)
3014   if (mng_info->have_global_sbit)
3015     {
3016       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
3017         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
3018     }
3019 #endif
3020   num_passes=png_set_interlace_handling(ping);
3021
3022   png_read_update_info(ping,ping_info);
3023
3024   ping_rowbytes=png_get_rowbytes(ping,ping_info);
3025
3026   /*
3027     Initialize image structure.
3028   */
3029   mng_info->image_box.left=0;
3030   mng_info->image_box.right=(ssize_t) ping_width;
3031   mng_info->image_box.top=0;
3032   mng_info->image_box.bottom=(ssize_t) ping_height;
3033   if (mng_info->mng_type == 0)
3034     {
3035       mng_info->mng_width=ping_width;
3036       mng_info->mng_height=ping_height;
3037       mng_info->frame=mng_info->image_box;
3038       mng_info->clip=mng_info->image_box;
3039     }
3040
3041   else
3042     {
3043       image->page.y=mng_info->y_off[mng_info->object_id];
3044     }
3045
3046   image->compression=ZipCompression;
3047   image->columns=ping_width;
3048   image->rows=ping_height;
3049
3050   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
3051       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
3052     {
3053       double
3054         image_gamma = image->gamma;
3055
3056       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3057          "    image->gamma=%f",(float) image_gamma);
3058
3059       if (image_gamma > 0.75)
3060         {
3061           /* Set image->rendering_intent to Undefined,
3062            * image->colorspace to GRAY, and reset image->chromaticity.
3063            */
3064           image->intensity = Rec709LuminancePixelIntensityMethod;
3065           SetImageColorspace(image,GRAYColorspace,exception);
3066         }
3067       else
3068         {
3069           RenderingIntent
3070             save_rendering_intent = image->rendering_intent;
3071           ChromaticityInfo
3072             save_chromaticity = image->chromaticity;
3073
3074           SetImageColorspace(image,GRAYColorspace,exception);
3075           image->rendering_intent = save_rendering_intent;
3076           image->chromaticity = save_chromaticity;
3077         }
3078
3079       image->gamma = image_gamma;
3080     }
3081
3082   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
3083       "    image->colorspace=%d",(int) image->colorspace);
3084
3085   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
3086       ((int) ping_bit_depth < 16 &&
3087       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
3088     {
3089       size_t
3090         one;
3091
3092       image->storage_class=PseudoClass;
3093       one=1;
3094       image->colors=one << ping_file_depth;
3095 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
3096       if (image->colors > 256)
3097         image->colors=256;
3098 #else
3099       if (image->colors > 65536L)
3100         image->colors=65536L;
3101 #endif
3102       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3103         {
3104           png_colorp
3105             palette;
3106
3107           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3108           image->colors=(size_t) number_colors;
3109
3110           if (logging != MagickFalse)
3111             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3112               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
3113         }
3114     }
3115
3116   if (image->storage_class == PseudoClass)
3117     {
3118       /*
3119         Initialize image colormap.
3120       */
3121       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3122         png_error(ping,"Memory allocation failed");
3123
3124       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3125         {
3126           png_colorp
3127             palette;
3128
3129           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3130
3131           for (i=0; i < (ssize_t) number_colors; i++)
3132           {
3133             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3134             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3135             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3136           }
3137
3138           for ( ; i < (ssize_t) image->colors; i++)
3139           {
3140             image->colormap[i].red=0;
3141             image->colormap[i].green=0;
3142             image->colormap[i].blue=0;
3143           }
3144         }
3145
3146       else
3147         {
3148           Quantum
3149             scale;
3150
3151           scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3152
3153 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3154           scale = ScaleShortToQuantum(scale);
3155 #endif
3156
3157           for (i=0; i < (ssize_t) image->colors; i++)
3158           {
3159             image->colormap[i].red=(Quantum) (i*scale);
3160             image->colormap[i].green=(Quantum) (i*scale);
3161             image->colormap[i].blue=(Quantum) (i*scale);
3162           }
3163        }
3164     }
3165
3166    /* Set some properties for reporting by "identify" */
3167     {
3168       char
3169         msg[MagickPathExtent];
3170
3171      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3172         ping_interlace_method in value */
3173
3174      (void) FormatLocaleString(msg,MagickPathExtent,
3175          "%d, %d",(int) ping_width, (int) ping_height);
3176      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3177
3178      (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3179         (int) ping_file_depth);
3180      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3181
3182      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3183          (int) ping_color_type,
3184          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3185      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3186
3187      if (ping_interlace_method == 0)
3188        {
3189          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3190             (int) ping_interlace_method);
3191        }
3192      else if (ping_interlace_method == 1)
3193        {
3194          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3195             (int) ping_interlace_method);
3196        }
3197      else
3198        {
3199          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3200             (int) ping_interlace_method);
3201        }
3202        (void) SetImageProperty(image,"png:IHDR.interlace_method",
3203          msg,exception);
3204
3205      if (number_colors != 0)
3206        {
3207          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3208             (int) number_colors);
3209          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3210             exception);
3211        }
3212    }
3213 #if defined(PNG_tIME_SUPPORTED)
3214    read_tIME_chunk(image,ping,ping_info,exception);
3215 #endif
3216
3217
3218   /*
3219     Read image scanlines.
3220   */
3221   if (image->delay != 0)
3222     mng_info->scenes_found++;
3223
3224   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3225       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3226       (image_info->first_scene+image_info->number_scenes))))
3227     {
3228       /* This happens later in non-ping decodes */
3229       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3230         image->storage_class=DirectClass;
3231       image->alpha_trait=
3232         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3233          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3234          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3235         BlendPixelTrait : UndefinedPixelTrait;
3236
3237       if (logging != MagickFalse)
3238         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3239           "    Skipping PNG image data for scene %.20g",(double)
3240           mng_info->scenes_found-1);
3241       png_destroy_read_struct(&ping,&ping_info,&end_info);
3242
3243 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3244       UnlockSemaphoreInfo(ping_semaphore);
3245 #endif
3246
3247       if (logging != MagickFalse)
3248         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3249           "  exit ReadOnePNGImage().");
3250
3251       return(image);
3252     }
3253
3254   if (logging != MagickFalse)
3255     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3256       "    Reading PNG IDAT chunk(s)");
3257
3258   status=SetImageExtent(image,image->columns,image->rows,exception);
3259   if (status == MagickFalse)
3260     {
3261       png_destroy_read_struct(&ping,&ping_info,&end_info);
3262 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3263       UnlockSemaphoreInfo(ping_semaphore);
3264 #endif
3265       return(DestroyImageList(image));
3266     }
3267
3268   if (num_passes > 1)
3269     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3270       sizeof(*ping_pixels));
3271   else
3272     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3273
3274   if (pixel_info == (MemoryInfo *) NULL)
3275     png_error(ping,"Memory allocation failed");
3276   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3277
3278   if (logging != MagickFalse)
3279     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3280       "    Converting PNG pixels to pixel packets");
3281   /*
3282     Convert PNG pixels to pixel packets.
3283   */
3284   quantum_info=AcquireQuantumInfo(image_info,image);
3285
3286   if (quantum_info == (QuantumInfo *) NULL)
3287      png_error(ping,"Failed to allocate quantum_info");
3288
3289   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3290
3291   {
3292
3293    MagickBooleanType
3294      found_transparent_pixel;
3295
3296   found_transparent_pixel=MagickFalse;
3297
3298   if (image->storage_class == DirectClass)
3299     {
3300       for (pass=0; pass < num_passes; pass++)
3301       {
3302         /*
3303           Convert image to DirectClass pixel packets.
3304         */
3305         image->alpha_trait=
3306             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3307             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3308             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3309             BlendPixelTrait : UndefinedPixelTrait;
3310
3311         for (y=0; y < (ssize_t) image->rows; y++)
3312         {
3313           if (num_passes > 1)
3314             row_offset=ping_rowbytes*y;
3315
3316           else
3317             row_offset=0;
3318
3319           png_read_row(ping,ping_pixels+row_offset,NULL);
3320
3321           if (pass < num_passes-1)
3322             continue;
3323
3324           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3325
3326           if (q == (Quantum *) NULL)
3327             break;
3328
3329           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3330             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3331               GrayQuantum,ping_pixels+row_offset,exception);
3332
3333           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3334             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3335               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3336
3337           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3338             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3339               RGBAQuantum,ping_pixels+row_offset,exception);
3340
3341           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3342             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3343               IndexQuantum,ping_pixels+row_offset,exception);
3344
3345           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3346             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3347               RGBQuantum,ping_pixels+row_offset,exception);
3348
3349           if (found_transparent_pixel == MagickFalse)
3350             {
3351               /* Is there a transparent pixel in the row? */
3352               if (y== 0 && logging != MagickFalse)
3353                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3354                    "    Looking for cheap transparent pixel");
3355
3356               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3357               {
3358                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3359                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3360                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3361                   {
3362                     if (logging != MagickFalse)
3363                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3364                         "    ...got one.");
3365
3366                     found_transparent_pixel = MagickTrue;
3367                     break;
3368                   }
3369                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3370                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3371                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3372                     transparent_color.red &&
3373                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3374                     transparent_color.green &&
3375                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3376                     transparent_color.blue))
3377                   {
3378                     if (logging != MagickFalse)
3379                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3380                         "    ...got one.");
3381                     found_transparent_pixel = MagickTrue;
3382                     break;
3383                   }
3384                 q+=GetPixelChannels(image);
3385               }
3386             }
3387
3388           if (num_passes == 1)
3389             {
3390               status=SetImageProgress(image,LoadImageTag,
3391                   (MagickOffsetType) y, image->rows);
3392
3393               if (status == MagickFalse)
3394                 break;
3395             }
3396           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3397             break;
3398         }
3399
3400         if (num_passes != 1)
3401           {
3402             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3403             if (status == MagickFalse)
3404               break;
3405           }
3406       }
3407     }
3408
3409   else /* image->storage_class != DirectClass */
3410
3411     for (pass=0; pass < num_passes; pass++)
3412     {
3413       register Quantum
3414         *r;
3415
3416       /*
3417         Convert grayscale image to PseudoClass pixel packets.
3418       */
3419       if (logging != MagickFalse)
3420         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3421           "    Converting grayscale pixels to pixel packets");
3422
3423       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3424         BlendPixelTrait : UndefinedPixelTrait;
3425
3426       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3427         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3428         sizeof(*quantum_scanline));
3429
3430       if (quantum_scanline == (Quantum *) NULL)
3431         png_error(ping,"Memory allocation failed");
3432
3433       for (y=0; y < (ssize_t) image->rows; y++)
3434       {
3435         Quantum
3436            alpha;
3437
3438         if (num_passes > 1)
3439           row_offset=ping_rowbytes*y;
3440
3441         else
3442           row_offset=0;
3443
3444         png_read_row(ping,ping_pixels+row_offset,NULL);
3445
3446         if (pass < num_passes-1)
3447           continue;
3448
3449         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3450
3451         if (q == (Quantum *) NULL)
3452           break;
3453
3454         p=ping_pixels+row_offset;
3455         r=quantum_scanline;
3456
3457         switch (ping_bit_depth)
3458         {
3459           case 8:
3460           {
3461
3462             if (ping_color_type == 4)
3463               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3464               {
3465                 *r++=*p++;
3466
3467                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3468
3469                 SetPixelAlpha(image,alpha,q);
3470
3471                 if (alpha != OpaqueAlpha)
3472                   found_transparent_pixel = MagickTrue;
3473
3474                 q+=GetPixelChannels(image);
3475               }
3476
3477             else
3478               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3479                 *r++=*p++;
3480
3481             break;
3482           }
3483
3484           case 16:
3485           {
3486             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3487             {
3488 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3489               unsigned short
3490                 quantum;
3491
3492               if (image->colors > 256)
3493                 quantum=(((unsigned int) *p++) << 8);
3494
3495               else
3496                 quantum=0;
3497
3498               quantum|=(*p++);
3499               *r=ScaleShortToQuantum(quantum);
3500               r++;
3501
3502               if (ping_color_type == 4)
3503                 {
3504                   if (image->colors > 256)
3505                     quantum=(((unsigned int) *p++) << 8);
3506                   else
3507                     quantum=0;
3508
3509                   quantum|=(*p++);
3510
3511                   alpha=ScaleShortToQuantum(quantum);
3512                   SetPixelAlpha(image,alpha,q);
3513
3514                   if (alpha != OpaqueAlpha)
3515                     found_transparent_pixel = MagickTrue;
3516
3517                   q+=GetPixelChannels(image);
3518                 }
3519
3520 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3521               *r++=(*p++);
3522               p++; /* strip low byte */
3523
3524               if (ping_color_type == 4)
3525                 {
3526                   SetPixelAlpha(image,*p++,q);
3527
3528                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3529                     found_transparent_pixel = MagickTrue;
3530
3531                   p++;
3532                   q+=GetPixelChannels(image);
3533                 }
3534 #endif
3535             }
3536
3537             break;
3538           }
3539
3540           default:
3541             break;
3542         }
3543
3544         /*
3545           Transfer image scanline.
3546         */
3547         r=quantum_scanline;
3548
3549         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3550
3551         if (q == (Quantum *) NULL)
3552           break;
3553         for (x=0; x < (ssize_t) image->columns; x++)
3554         {
3555           SetPixelIndex(image,*r++,q);
3556           q+=GetPixelChannels(image);
3557         }
3558
3559         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3560           break;
3561
3562         if (num_passes == 1)
3563           {
3564             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3565               image->rows);
3566
3567             if (status == MagickFalse)
3568               break;
3569           }
3570       }
3571
3572       if (num_passes != 1)
3573         {
3574           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3575
3576           if (status == MagickFalse)
3577             break;
3578         }
3579
3580       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3581     }
3582
3583     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3584       UndefinedPixelTrait;
3585
3586     if (logging != MagickFalse)
3587       {
3588         if (found_transparent_pixel != MagickFalse)
3589           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3590             "    Found transparent pixel");
3591         else
3592           {
3593             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3594               "    No transparent pixel was found");
3595
3596             ping_color_type&=0x03;
3597           }
3598       }
3599     }
3600
3601   quantum_info=DestroyQuantumInfo(quantum_info);
3602
3603   if (image->storage_class == PseudoClass)
3604     {
3605       PixelTrait
3606         alpha_trait;
3607
3608       alpha_trait=image->alpha_trait;
3609       image->alpha_trait=UndefinedPixelTrait;
3610       (void) SyncImage(image,exception);
3611       image->alpha_trait=alpha_trait;
3612     }
3613
3614   png_read_end(ping,end_info);
3615
3616   if (logging != MagickFalse)
3617   {
3618     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3619        "  image->storage_class=%d\n",(int) image->storage_class);
3620   }
3621
3622   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3623       (ssize_t) image_info->first_scene && image->delay != 0)
3624     {
3625       png_destroy_read_struct(&ping,&ping_info,&end_info);
3626       pixel_info=RelinquishVirtualMemory(pixel_info);
3627       image->colors=2;
3628       (void) SetImageBackgroundColor(image,exception);
3629 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3630       UnlockSemaphoreInfo(ping_semaphore);
3631 #endif
3632       if (logging != MagickFalse)
3633         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3634           "  exit ReadOnePNGImage() early.");
3635       return(image);
3636     }
3637
3638   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3639     {
3640       ClassType
3641         storage_class;
3642
3643       /*
3644         Image has a transparent background.
3645       */
3646       storage_class=image->storage_class;
3647       image->alpha_trait=BlendPixelTrait;
3648
3649 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3650
3651       if (storage_class == PseudoClass)
3652         {
3653           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3654             {
3655               for (x=0; x < ping_num_trans; x++)
3656               {
3657                  image->colormap[x].alpha_trait=BlendPixelTrait;
3658                  image->colormap[x].alpha =
3659                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3660               }
3661             }
3662
3663           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3664             {
3665               for (x=0; x < (int) image->colors; x++)
3666               {
3667                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3668                      transparent_color.alpha)
3669                  {
3670                     image->colormap[x].alpha_trait=BlendPixelTrait;
3671                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3672                  }
3673               }
3674             }
3675           (void) SyncImage(image,exception);
3676         }
3677
3678 #if 1 /* Should have already been done above, but glennrp problem P10
3679        * needs this.
3680        */
3681       else
3682         {
3683           for (y=0; y < (ssize_t) image->rows; y++)
3684           {
3685             image->storage_class=storage_class;
3686             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3687
3688             if (q == (Quantum *) NULL)
3689               break;
3690
3691
3692             /* Caution: on a Q8 build, this does not distinguish between
3693              * 16-bit colors that differ only in the low byte
3694              */
3695             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3696             {
3697               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3698                   transparent_color.red &&
3699                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3700                   transparent_color.green &&
3701                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3702                   transparent_color.blue)
3703                 {
3704                   SetPixelAlpha(image,TransparentAlpha,q);
3705                 }
3706
3707 #if 0 /* I have not found a case where this is needed. */
3708               else
3709                 {
3710                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3711                 }
3712 #endif
3713
3714               q+=GetPixelChannels(image);
3715             }
3716
3717             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3718                break;
3719           }
3720         }
3721 #endif
3722
3723       image->storage_class=DirectClass;
3724     }
3725
3726   for (j = 0; j < 2; j++)
3727   {
3728     if (j == 0)
3729       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3730           MagickTrue : MagickFalse;
3731     else
3732       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3733           MagickTrue : MagickFalse;
3734
3735     if (status != MagickFalse)
3736       for (i=0; i < (ssize_t) num_text; i++)
3737       {
3738         /* Check for a profile */
3739
3740         if (logging != MagickFalse)
3741           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3742             "    Reading PNG text chunk");
3743
3744         if (strlen(text[i].key) > 16 &&
3745             memcmp(text[i].key, "Raw profile type ",17) == 0)
3746           {
3747             const char
3748               *value;
3749
3750             value=GetImageOption(image_info,"profile:skip");
3751
3752             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3753             {
3754                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3755                   (int) i,exception);
3756                num_raw_profiles++;
3757                if (logging != MagickFalse)
3758                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3759                    "    Read raw profile %s",text[i].key+17);
3760             }
3761             else
3762             {
3763                if (logging != MagickFalse)
3764                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3765                    "    Skipping raw profile %s",text[i].key+17);
3766             }
3767           }
3768
3769         else
3770           {
3771             char
3772               *value;
3773
3774             length=text[i].text_length;
3775             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3776               sizeof(*value));
3777             if (value == (char *) NULL)
3778               {
3779                 png_error(ping,"Memory allocation failed");
3780                 break;
3781               }
3782             *value='\0';
3783             (void) ConcatenateMagickString(value,text[i].text,length+2);
3784
3785             /* Don't save "density" or "units" property if we have a pHYs
3786              * chunk
3787              */
3788             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3789                 (LocaleCompare(text[i].key,"density") != 0 &&
3790                 LocaleCompare(text[i].key,"units") != 0))
3791                (void) SetImageProperty(image,text[i].key,value,exception);
3792
3793             if (logging != MagickFalse)
3794             {
3795               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3796                 "      length: %lu\n"
3797                 "      Keyword: %s",
3798                 (unsigned long) length,
3799                 text[i].key);
3800             }
3801
3802             value=DestroyString(value);
3803           }
3804       }
3805     num_text_total += num_text;
3806   }
3807
3808 #ifdef MNG_OBJECT_BUFFERS
3809   /*
3810     Store the object if necessary.
3811   */
3812   if (object_id && !mng_info->frozen[object_id])
3813     {
3814       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3815         {
3816           /*
3817             create a new object buffer.
3818           */
3819           mng_info->ob[object_id]=(MngBuffer *)
3820             AcquireMagickMemory(sizeof(MngBuffer));
3821
3822           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3823             {
3824               mng_info->ob[object_id]->image=(Image *) NULL;
3825               mng_info->ob[object_id]->reference_count=1;
3826             }
3827         }
3828
3829       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3830           mng_info->ob[object_id]->frozen)
3831         {
3832           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3833              png_error(ping,"Memory allocation failed");
3834
3835           if (mng_info->ob[object_id]->frozen)
3836             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3837         }
3838
3839       else
3840         {
3841
3842           if (mng_info->ob[object_id]->image != (Image *) NULL)
3843             mng_info->ob[object_id]->image=DestroyImage
3844                 (mng_info->ob[object_id]->image);
3845
3846           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3847             exception);
3848
3849           if (mng_info->ob[object_id]->image != (Image *) NULL)
3850             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3851
3852           else
3853             png_error(ping, "Cloning image for object buffer failed");
3854
3855           if (ping_width > 250000L || ping_height > 250000L)
3856              png_error(ping,"PNG Image dimensions are too large.");
3857
3858           mng_info->ob[object_id]->width=ping_width;
3859           mng_info->ob[object_id]->height=ping_height;
3860           mng_info->ob[object_id]->color_type=ping_color_type;
3861           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3862           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3863           mng_info->ob[object_id]->compression_method=
3864              ping_compression_method;
3865           mng_info->ob[object_id]->filter_method=ping_filter_method;
3866
3867           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3868             {
3869               png_colorp
3870                 plte;
3871
3872               /*
3873                 Copy the PLTE to the object buffer.
3874               */
3875               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3876               mng_info->ob[object_id]->plte_length=number_colors;
3877
3878               for (i=0; i < number_colors; i++)
3879               {
3880                 mng_info->ob[object_id]->plte[i]=plte[i];
3881               }
3882             }
3883
3884           else
3885               mng_info->ob[object_id]->plte_length=0;
3886         }
3887     }
3888 #endif
3889
3890    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3891     * alpha or if a valid tRNS chunk is present, no matter whether there
3892     * is actual transparency present.
3893     */
3894     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3895         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3896         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3897         BlendPixelTrait : UndefinedPixelTrait;
3898
3899 #if 0  /* I'm not sure what's wrong here but it does not work. */
3900     if (image->alpha_trait != UndefinedPixelTrait)
3901     {
3902       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3903         (void) SetImageType(image,GrayscaleAlphaType,exception);
3904
3905       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3906         (void) SetImageType(image,PaletteAlphaType,exception);
3907
3908       else
3909         (void) SetImageType(image,TrueColorAlphaType,exception);
3910     }
3911
3912     else
3913     {
3914       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3915         (void) SetImageType(image,GrayscaleType,exception);
3916
3917       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3918         (void) SetImageType(image,PaletteType,exception);
3919
3920       else
3921         (void) SetImageType(image,TrueColorType,exception);
3922     }
3923 #endif
3924
3925    /* Set more properties for identify to retrieve */
3926    {
3927      char
3928        msg[MagickPathExtent];
3929
3930      if (num_text_total != 0)
3931        {
3932          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3933          (void) FormatLocaleString(msg,MagickPathExtent,
3934             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3935          (void) SetImageProperty(image,"png:text",msg,
3936                 exception);
3937        }
3938
3939      if (num_raw_profiles != 0)
3940        {
3941          (void) FormatLocaleString(msg,MagickPathExtent,
3942             "%d were found", num_raw_profiles);
3943          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3944                 exception);
3945        }
3946
3947      /* cHRM chunk: */
3948      if (ping_found_cHRM != MagickFalse)
3949        {
3950          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3951             "chunk was found (see Chromaticity, above)");
3952          (void) SetImageProperty(image,"png:cHRM",msg,
3953                 exception);
3954        }
3955
3956      /* bKGD chunk: */
3957      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3958        {
3959          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3960             "chunk was found (see Background color, above)");
3961          (void) SetImageProperty(image,"png:bKGD",msg,
3962                 exception);
3963        }
3964
3965      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3966         "chunk was found");
3967
3968 #if defined(PNG_iCCP_SUPPORTED)
3969      /* iCCP chunk: */
3970      if (ping_found_iCCP != MagickFalse)
3971         (void) SetImageProperty(image,"png:iCCP",msg,
3972                 exception);
3973 #endif
3974
3975      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3976         (void) SetImageProperty(image,"png:tRNS",msg,
3977                 exception);
3978
3979 #if defined(PNG_sRGB_SUPPORTED)
3980      /* sRGB chunk: */
3981      if (ping_found_sRGB != MagickFalse)
3982        {
3983          (void) FormatLocaleString(msg,MagickPathExtent,
3984             "intent=%d (%s)",
3985             (int) intent,
3986             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3987          (void) SetImageProperty(image,"png:sRGB",msg,
3988                  exception);
3989        }
3990 #endif
3991
3992      /* gAMA chunk: */
3993      if (ping_found_gAMA != MagickFalse)
3994        {
3995          (void) FormatLocaleString(msg,MagickPathExtent,
3996             "gamma=%.8g (See Gamma, above)",
3997             file_gamma);
3998          (void) SetImageProperty(image,"png:gAMA",msg,
3999                 exception);
4000        }
4001
4002 #if defined(PNG_pHYs_SUPPORTED)
4003      /* pHYs chunk: */
4004      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
4005        {
4006          (void) FormatLocaleString(msg,MagickPathExtent,
4007             "x_res=%.10g, y_res=%.10g, units=%d",
4008             (double) x_resolution,(double) y_resolution, unit_type);
4009          (void) SetImageProperty(image,"png:pHYs",msg,
4010                 exception);
4011        }
4012 #endif
4013
4014 #if defined(PNG_oFFs_SUPPORTED)
4015      /* oFFs chunk: */
4016      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
4017        {
4018          (void) FormatLocaleString(msg,MagickPathExtent,
4019             "x_off=%.20g, y_off=%.20g",
4020             (double) image->page.x,(double) image->page.y);
4021          (void) SetImageProperty(image,"png:oFFs",msg,
4022                 exception);
4023        }
4024 #endif
4025
4026 #if defined(PNG_tIME_SUPPORTED)
4027      read_tIME_chunk(image,ping,end_info,exception);
4028 #endif
4029
4030      /* caNv chunk: */
4031      if ((image->page.width != 0 && image->page.width != image->columns) ||
4032          (image->page.height != 0 && image->page.height != image->rows) ||
4033          (image->page.x != 0 || image->page.y != 0))
4034        {
4035          (void) FormatLocaleString(msg,MagickPathExtent,
4036             "width=%.20g, height=%.20g, x_offset=%.20g, y_offset=%.20g",
4037             (double) image->page.width,(double) image->page.height,
4038             (double) image->page.x,(double) image->page.y);
4039          (void) SetImageProperty(image,"png:caNv",msg,
4040                 exception);
4041        }
4042
4043      /* vpAg chunk: */
4044      if ((image->page.width != 0 && image->page.width != image->columns) ||
4045          (image->page.height != 0 && image->page.height != image->rows))
4046        {
4047          (void) FormatLocaleString(msg,MagickPathExtent,
4048             "width=%.20g, height=%.20g",
4049             (double) image->page.width,(double) image->page.height);
4050          (void) SetImageProperty(image,"png:vpAg",msg,
4051                 exception);
4052        }
4053    }
4054
4055   /*
4056     Relinquish resources.
4057   */
4058   png_destroy_read_struct(&ping,&ping_info,&end_info);
4059
4060   pixel_info=RelinquishVirtualMemory(pixel_info);
4061
4062   if (logging != MagickFalse)
4063     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4064       "  exit ReadOnePNGImage()");
4065
4066 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
4067   UnlockSemaphoreInfo(ping_semaphore);
4068 #endif
4069
4070   /* }  for navigation to beginning of SETJMP-protected block, revert to
4071    *    Throwing an Exception when an error occurs.
4072    */
4073
4074   return(image);
4075
4076 /* end of reading one PNG image */
4077 }
4078
4079 static Image *ReadPNGImage(const ImageInfo *image_info,
4080   ExceptionInfo *exception)
4081 {
4082   Image
4083     *image;
4084
4085   MagickBooleanType
4086     logging,
4087     status;
4088
4089   MngInfo
4090     *mng_info;
4091
4092   char
4093     magic_number[MagickPathExtent];
4094
4095   ssize_t
4096     count;
4097
4098   /*
4099     Open image file.
4100   */
4101   assert(image_info != (const ImageInfo *) NULL);
4102   assert(image_info->signature == MagickCoreSignature);
4103
4104   if (image_info->debug != MagickFalse)
4105     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
4106       image_info->filename);
4107
4108   assert(exception != (ExceptionInfo *) NULL);
4109   assert(exception->signature == MagickCoreSignature);
4110   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
4111   image=AcquireImage(image_info,exception);
4112   mng_info=(MngInfo *) NULL;
4113   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4114
4115   if (status == MagickFalse)
4116     ThrowReaderException(FileOpenError,"UnableToOpenFile");
4117
4118   /*
4119     Verify PNG signature.
4120   */
4121   count=ReadBlob(image,8,(unsigned char *) magic_number);
4122
4123   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
4124     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4125
4126   /*
4127      Verify that file size large enough to contain a PNG datastream.
4128   */
4129   if (GetBlobSize(image) < 61)
4130     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
4131
4132   /*
4133     Allocate a MngInfo structure.
4134   */
4135   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
4136
4137   if (mng_info == (MngInfo *) NULL)
4138     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4139
4140   /*
4141     Initialize members of the MngInfo structure.
4142   */
4143   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4144   mng_info->image=image;
4145
4146   image=ReadOnePNGImage(mng_info,image_info,exception);
4147   mng_info=MngInfoFreeStruct(mng_info);
4148
4149   if (image == (Image *) NULL)
4150     {
4151       if (logging != MagickFalse)
4152         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4153           "exit ReadPNGImage() with error");
4154
4155       return((Image *) NULL);
4156     }
4157
4158   (void) CloseBlob(image);
4159
4160   if ((image->columns == 0) || (image->rows == 0))
4161     {
4162       if (logging != MagickFalse)
4163         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4164           "exit ReadPNGImage() with error.");
4165
4166       ThrowReaderException(CorruptImageError,"CorruptImage");
4167     }
4168
4169   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4170       ((image->gamma < .45) || (image->gamma > .46)) &&
4171            !(image->chromaticity.red_primary.x>0.6399f &&
4172            image->chromaticity.red_primary.x<0.6401f &&
4173            image->chromaticity.red_primary.y>0.3299f &&
4174            image->chromaticity.red_primary.y<0.3301f &&
4175            image->chromaticity.green_primary.x>0.2999f &&
4176            image->chromaticity.green_primary.x<0.3001f &&
4177            image->chromaticity.green_primary.y>0.5999f &&
4178            image->chromaticity.green_primary.y<0.6001f &&
4179            image->chromaticity.blue_primary.x>0.1499f &&
4180            image->chromaticity.blue_primary.x<0.1501f &&
4181            image->chromaticity.blue_primary.y>0.0599f &&
4182            image->chromaticity.blue_primary.y<0.0601f &&
4183            image->chromaticity.white_point.x>0.3126f &&
4184            image->chromaticity.white_point.x<0.3128f &&
4185            image->chromaticity.white_point.y>0.3289f &&
4186            image->chromaticity.white_point.y<0.3291f))
4187     {
4188        SetImageColorspace(image,RGBColorspace,exception);
4189     }
4190
4191   if (logging != MagickFalse)
4192     {
4193        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4194            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4195                (double) image->page.width,(double) image->page.height,
4196                (double) image->page.x,(double) image->page.y);
4197        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4198            "  image->colorspace: %d", (int) image->colorspace);
4199     }
4200
4201   if (logging != MagickFalse)
4202     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4203
4204   return(image);
4205 }
4206
4207
4208
4209 #if defined(JNG_SUPPORTED)
4210 /*
4211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4212 %                                                                             %
4213 %                                                                             %
4214 %                                                                             %
4215 %   R e a d O n e J N G I m a g e                                             %
4216 %                                                                             %
4217 %                                                                             %
4218 %                                                                             %
4219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4220 %
4221 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4222 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4223 %  necessary for the new Image structure and returns a pointer to the new
4224 %  image.
4225 %
4226 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4227 %
4228 %  The format of the ReadOneJNGImage method is:
4229 %
4230 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4231 %         ExceptionInfo *exception)
4232 %
4233 %  A description of each parameter follows:
4234 %
4235 %    o mng_info: Specifies a pointer to a MngInfo structure.
4236 %
4237 %    o image_info: the image info.
4238 %
4239 %    o exception: return any errors or warnings in this structure.
4240 %
4241 */
4242 void
4243 DestroyJNG(unsigned char *chunk,Image **color_image,
4244    ImageInfo **color_image_info,
4245    Image **alpha_image,ImageInfo **alpha_image_info)
4246 {
4247   (void) RelinquishMagickMemory(chunk);
4248   if (*color_image_info)
4249   {
4250     DestroyImageInfo(*color_image_info);
4251     *color_image_info = (ImageInfo *)NULL;
4252   }
4253   if (*alpha_image_info)
4254   {
4255     DestroyImageInfo(*alpha_image_info);
4256     *alpha_image_info = (ImageInfo *)NULL;
4257   }
4258   if (*color_image)
4259   {
4260     DestroyImage(*color_image);
4261     *color_image = (Image *)NULL;
4262   }
4263   if (*alpha_image)
4264   {
4265     DestroyImage(*alpha_image);
4266     *alpha_image = (Image *)NULL;
4267   }
4268 }
4269 static Image *ReadOneJNGImage(MngInfo *mng_info,
4270     const ImageInfo *image_info, ExceptionInfo *exception)
4271 {
4272   Image
4273     *alpha_image,
4274     *color_image,
4275     *image,
4276     *jng_image;
4277
4278   ImageInfo
4279     *alpha_image_info,
4280     *color_image_info;
4281
4282   MagickBooleanType
4283     logging;
4284
4285   ssize_t
4286     y;
4287
4288   MagickBooleanType
4289     status;
4290
4291   png_uint_32
4292     jng_height,
4293     jng_width;
4294
4295   png_byte
4296     jng_color_type,
4297     jng_image_sample_depth,
4298     jng_image_compression_method,
4299     jng_image_interlace_method,
4300     jng_alpha_sample_depth,
4301     jng_alpha_compression_method,
4302     jng_alpha_filter_method,
4303     jng_alpha_interlace_method;
4304
4305   register const Quantum
4306     *s;
4307
4308   register ssize_t
4309     i,
4310     x;
4311
4312   register Quantum
4313     *q;
4314
4315   register unsigned char
4316     *p;
4317
4318   unsigned int
4319     read_JSEP,
4320     reading_idat;
4321
4322   size_t
4323     length;
4324
4325   jng_alpha_compression_method=0;
4326   jng_alpha_sample_depth=8;
4327   jng_color_type=0;
4328   jng_height=0;
4329   jng_width=0;
4330   alpha_image=(Image *) NULL;
4331   color_image=(Image *) NULL;
4332   alpha_image_info=(ImageInfo *) NULL;
4333   color_image_info=(ImageInfo *) NULL;
4334
4335   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4336     "  Enter ReadOneJNGImage()");
4337
4338   image=mng_info->image;
4339
4340   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4341     {
4342       /*
4343         Allocate next image structure.
4344       */
4345       if (logging != MagickFalse)
4346         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4347            "  AcquireNextImage()");
4348
4349       AcquireNextImage(image_info,image,exception);
4350
4351       if (GetNextImageInList(image) == (Image *) NULL)
4352         return(DestroyImageList(image));
4353
4354       image=SyncNextImageInList(image);
4355     }
4356   mng_info->image=image;
4357
4358   /*
4359     Signature bytes have already been read.
4360   */
4361
4362   read_JSEP=MagickFalse;
4363   reading_idat=MagickFalse;
4364   for (;;)
4365   {
4366     char
4367       type[MagickPathExtent];
4368
4369     unsigned char
4370       *chunk;
4371
4372     unsigned int
4373       count;
4374
4375     /*
4376       Read a new JNG chunk.
4377     */
4378     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4379       2*GetBlobSize(image));
4380
4381     if (status == MagickFalse)
4382       break;
4383
4384     type[0]='\0';
4385     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4386     length=ReadBlobMSBLong(image);
4387     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4388
4389     if (logging != MagickFalse)
4390       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4391         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4392         type[0],type[1],type[2],type[3],(double) length);
4393
4394     if (length > PNG_UINT_31_MAX || count == 0)
4395       {
4396         DestroyJNG(NULL,&color_image,&color_image_info,
4397           &alpha_image,&alpha_image_info);
4398         ThrowReaderException(CorruptImageError,"CorruptImage");
4399       }
4400
4401     p=NULL;
4402     chunk=(unsigned char *) NULL;
4403
4404     if (length != 0)
4405       {
4406         if (length > GetBlobSize(image))
4407           {
4408             DestroyJNG(NULL,&color_image,&color_image_info,NULL,NULL);
4409             ThrowReaderException(CorruptImageError,
4410               "InsufficientImageDataInFile");
4411           }
4412         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4413
4414         if (chunk == (unsigned char *) NULL)
4415           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4416
4417         for (i=0; i < (ssize_t) length; i++)
4418         {
4419           int
4420             c;
4421
4422           c=ReadBlobByte(image);
4423           if (c == EOF)
4424             break;
4425           chunk[i]=(unsigned char) c;
4426         }
4427
4428         p=chunk;
4429       }
4430
4431     (void) ReadBlobMSBLong(image);  /* read crc word */
4432
4433     if (memcmp(type,mng_JHDR,4) == 0)
4434       {
4435         if (length == 16)
4436           {
4437             jng_width=(png_uint_32)mng_get_long(p);
4438             jng_height=(png_uint_32)mng_get_long(&p[4]);
4439             if ((jng_width == 0) || (jng_height == 0))
4440               ThrowReaderException(CorruptImageError,
4441                 "NegativeOrZeroImageSize");
4442             jng_color_type=p[8];
4443             jng_image_sample_depth=p[9];
4444             jng_image_compression_method=p[10];
4445             jng_image_interlace_method=p[11];
4446
4447             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4448               NoInterlace;
4449
4450             jng_alpha_sample_depth=p[12];
4451             jng_alpha_compression_method=p[13];
4452             jng_alpha_filter_method=p[14];
4453             jng_alpha_interlace_method=p[15];
4454
4455             if (logging != MagickFalse)
4456               {
4457                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4458                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4459                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4460                   "    jng_image_compression_method:%3d",
4461                   (unsigned long) jng_width, (unsigned long) jng_height,
4462                   jng_color_type, jng_image_sample_depth,
4463                   jng_image_compression_method);
4464
4465                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4466                   "    jng_image_interlace_method:  %3d"
4467                   "    jng_alpha_sample_depth:      %3d",
4468                   jng_image_interlace_method,
4469                   jng_alpha_sample_depth);
4470
4471                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4472                   "    jng_alpha_compression_method:%3d\n"
4473                   "    jng_alpha_filter_method:     %3d\n"
4474                   "    jng_alpha_interlace_method:  %3d",
4475                   jng_alpha_compression_method,
4476                   jng_alpha_filter_method,
4477                   jng_alpha_interlace_method);
4478               }
4479           }
4480
4481         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4482
4483         if (jng_width > 65535 || jng_height > 65535 ||
4484              (long) jng_width > GetMagickResourceLimit(WidthResource) ||
4485              (long) jng_height > GetMagickResourceLimit(HeightResource))
4486           {
4487             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4488                "    JNG width or height too large: (%lu x %lu)",
4489                 (long) jng_width, (long) jng_height);
4490             DestroyJNG(chunk,&color_image,&color_image_info,
4491               &alpha_image,&alpha_image_info);
4492             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4493           }
4494
4495         continue;
4496       }
4497
4498
4499     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4500         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4501          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4502       {
4503         /*
4504            o create color_image
4505            o open color_blob, attached to color_image
4506            o if (color type has alpha)
4507                open alpha_blob, attached to alpha_image
4508         */
4509
4510         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4511
4512         if (color_image_info == (ImageInfo *) NULL)
4513           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4514
4515         GetImageInfo(color_image_info);
4516         color_image=AcquireImage(color_image_info,exception);
4517
4518         if (color_image == (Image *) NULL)
4519           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4520
4521         if (logging != MagickFalse)
4522           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4523             "    Creating color_blob.");
4524
4525         (void) AcquireUniqueFilename(color_image->filename);
4526         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4527           exception);
4528
4529         if (status == MagickFalse)
4530           {
4531             color_image=DestroyImage(color_image);
4532             return(DestroyImageList(image));
4533           }
4534
4535         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4536           {
4537             alpha_image_info=(ImageInfo *)
4538               AcquireMagickMemory(sizeof(ImageInfo));
4539
4540             if (alpha_image_info == (ImageInfo *) NULL)
4541               {
4542                 color_image=DestroyImage(color_image);
4543                 ThrowReaderException(ResourceLimitError,
4544                   "MemoryAllocationFailed");
4545               }
4546
4547             GetImageInfo(alpha_image_info);
4548             alpha_image=AcquireImage(alpha_image_info,exception);
4549
4550             if (alpha_image == (Image *) NULL)
4551               {
4552                 alpha_image_info=DestroyImageInfo(alpha_image_info);
4553                 color_image=DestroyImage(color_image);
4554                 ThrowReaderException(ResourceLimitError,
4555                   "MemoryAllocationFailed");
4556               }
4557
4558             if (logging != MagickFalse)
4559               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4560                 "    Creating alpha_blob.");
4561
4562             (void) AcquireUniqueFilename(alpha_image->filename);
4563             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4564               exception);
4565
4566             if (status == MagickFalse)
4567               {
4568                 alpha_image=DestroyImage(alpha_image);
4569                 alpha_image_info=DestroyImageInfo(alpha_image_info);
4570                 color_image=DestroyImage(color_image);
4571                 return(DestroyImageList(image));
4572               }
4573
4574             if (jng_alpha_compression_method == 0)
4575               {
4576                 unsigned char
4577                   data[18];
4578
4579                 if (logging != MagickFalse)
4580                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4581                     "    Writing IHDR chunk to alpha_blob.");
4582
4583                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4584                   "\211PNG\r\n\032\n");
4585
4586                 (void) WriteBlobMSBULong(alpha_image,13L);
4587                 PNGType(data,mng_IHDR);
4588                 LogPNGChunk(logging,mng_IHDR,13L);
4589                 PNGLong(data+4,jng_width);
4590                 PNGLong(data+8,jng_height);
4591                 data[12]=jng_alpha_sample_depth;
4592                 data[13]=0; /* color_type gray */
4593                 data[14]=0; /* compression method 0 */
4594                 data[15]=0; /* filter_method 0 */
4595                 data[16]=0; /* interlace_method 0 */
4596                 (void) WriteBlob(alpha_image,17,data);
4597                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4598               }
4599           }
4600         reading_idat=MagickTrue;
4601       }
4602
4603     if (memcmp(type,mng_JDAT,4) == 0)
4604       {
4605         /* Copy chunk to color_image->blob */
4606
4607         if (logging != MagickFalse)
4608           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4609             "    Copying JDAT chunk data to color_blob.");
4610
4611         if (length != 0)
4612           {
4613             (void) WriteBlob(color_image,length,chunk);
4614             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4615           }
4616
4617         continue;
4618       }
4619
4620     if (memcmp(type,mng_IDAT,4) == 0)
4621       {
4622         png_byte
4623            data[5];
4624
4625         /* Copy IDAT header and chunk data to alpha_image->blob */
4626
4627         if (alpha_image != NULL && image_info->ping == MagickFalse)
4628           {
4629             if (logging != MagickFalse)
4630               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4631                 "    Copying IDAT chunk data to alpha_blob.");
4632
4633             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4634             PNGType(data,mng_IDAT);
4635             LogPNGChunk(logging,mng_IDAT,length);
4636             (void) WriteBlob(alpha_image,4,data);
4637             (void) WriteBlob(alpha_image,length,chunk);
4638             (void) WriteBlobMSBULong(alpha_image,
4639               crc32(crc32(0,data,4),chunk,(uInt) length));
4640           }
4641
4642         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4643
4644         continue;
4645       }
4646
4647     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4648       {
4649         /* Copy chunk data to alpha_image->blob */
4650
4651         if (alpha_image != NULL && image_info->ping == MagickFalse)
4652           {
4653             if (logging != MagickFalse)
4654               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4655                 "    Copying JDAA chunk data to alpha_blob.");
4656
4657             (void) WriteBlob(alpha_image,length,chunk);
4658           }
4659
4660         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4661
4662         continue;
4663       }
4664
4665     if (memcmp(type,mng_JSEP,4) == 0)
4666       {
4667         read_JSEP=MagickTrue;
4668
4669         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4670
4671         continue;
4672       }
4673
4674     if (memcmp(type,mng_bKGD,4) == 0)
4675       {
4676         if (length == 2)
4677           {
4678             image->background_color.red=ScaleCharToQuantum(p[1]);
4679             image->background_color.green=image->background_color.red;
4680             image->background_color.blue=image->background_color.red;
4681           }
4682
4683         if (length == 6)
4684           {
4685             image->background_color.red=ScaleCharToQuantum(p[1]);
4686             image->background_color.green=ScaleCharToQuantum(p[3]);
4687             image->background_color.blue=ScaleCharToQuantum(p[5]);
4688           }
4689
4690         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4691         continue;
4692       }
4693
4694     if (memcmp(type,mng_gAMA,4) == 0)
4695       {
4696         if (length == 4)
4697           image->gamma=((float) mng_get_long(p))*0.00001;
4698
4699         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4700         continue;
4701       }
4702
4703     if (memcmp(type,mng_cHRM,4) == 0)
4704       {
4705         if (length == 32)
4706           {
4707             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4708             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4709             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4710             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4711             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4712             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4713             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4714             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4715           }
4716
4717         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4718         continue;
4719       }
4720
4721     if (memcmp(type,mng_sRGB,4) == 0)
4722       {
4723         if (length == 1)
4724           {
4725             image->rendering_intent=
4726               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4727             image->gamma=1.000f/2.200f;
4728             image->chromaticity.red_primary.x=0.6400f;
4729             image->chromaticity.red_primary.y=0.3300f;
4730             image->chromaticity.green_primary.x=0.3000f;
4731             image->chromaticity.green_primary.y=0.6000f;
4732             image->chromaticity.blue_primary.x=0.1500f;
4733             image->chromaticity.blue_primary.y=0.0600f;
4734             image->chromaticity.white_point.x=0.3127f;
4735             image->chromaticity.white_point.y=0.3290f;
4736           }
4737
4738         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4739         continue;
4740       }
4741
4742     if (memcmp(type,mng_oFFs,4) == 0)
4743       {
4744         if (length > 8)
4745           {
4746             image->page.x=(ssize_t) mng_get_long(p);
4747             image->page.y=(ssize_t) mng_get_long(&p[4]);
4748
4749             if ((int) p[8] != 0)
4750               {
4751                 image->page.x/=10000;
4752                 image->page.y/=10000;
4753               }
4754           }
4755
4756         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4757
4758         continue;
4759       }
4760
4761     if (memcmp(type,mng_pHYs,4) == 0)
4762       {
4763         if (length > 8)
4764           {
4765             image->resolution.x=(double) mng_get_long(p);
4766             image->resolution.y=(double) mng_get_long(&p[4]);
4767             if ((int) p[8] == PNG_RESOLUTION_METER)
4768               {
4769                 image->units=PixelsPerCentimeterResolution;
4770                 image->resolution.x=image->resolution.x/100.0f;
4771                 image->resolution.y=image->resolution.y/100.0f;
4772               }
4773           }
4774
4775         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4776         continue;
4777       }
4778
4779 #if 0
4780     if (memcmp(type,mng_iCCP,4) == 0)
4781       {
4782         /* To do: */
4783         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4784
4785         continue;
4786       }
4787 #endif
4788
4789     chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4790
4791     if (memcmp(type,mng_IEND,4))
4792       continue;
4793
4794     break;
4795   }
4796
4797
4798   /* IEND found */
4799
4800   /*
4801     Finish up reading image data:
4802
4803        o read main image from color_blob.
4804
4805        o close color_blob.
4806
4807        o if (color_type has alpha)
4808             if alpha_encoding is PNG
4809                read secondary image from alpha_blob via ReadPNG
4810             if alpha_encoding is JPEG
4811                read secondary image from alpha_blob via ReadJPEG
4812
4813        o close alpha_blob.
4814
4815        o copy intensity of secondary image into
4816          alpha samples of main image.
4817
4818        o destroy the secondary image.
4819   */
4820
4821   if (color_image_info == (ImageInfo *) NULL)
4822     {
4823       assert(color_image == (Image *) NULL);
4824       assert(alpha_image == (Image *) NULL);
4825       return(DestroyImageList(image));
4826     }
4827
4828   if (color_image == (Image *) NULL)
4829     {
4830       assert(alpha_image == (Image *) NULL);
4831       return(DestroyImageList(image));
4832     }
4833
4834   (void) SeekBlob(color_image,0,SEEK_SET);
4835
4836   if (logging != MagickFalse)
4837     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4838       "    Reading jng_image from color_blob.");
4839
4840   assert(color_image_info != (ImageInfo *) NULL);
4841   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4842     color_image->filename);
4843
4844   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4845   jng_image=ReadImage(color_image_info,exception);
4846
4847   (void) RelinquishUniqueFileResource(color_image->filename);
4848   color_image=DestroyImage(color_image);
4849   color_image_info=DestroyImageInfo(color_image_info);
4850
4851   if (jng_image == (Image *) NULL)
4852     return(DestroyImageList(image));
4853
4854   if (logging != MagickFalse)
4855     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4856       "    Copying jng_image pixels to main image.");
4857
4858   image->rows=jng_height;
4859   image->columns=jng_width;
4860
4861   status=SetImageExtent(image,image->columns,image->rows,exception);
4862   if (status == MagickFalse)
4863     return(DestroyImageList(image));
4864
4865   for (y=0; y < (ssize_t) image->rows; y++)
4866   {
4867     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4868     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4869     for (x=(ssize_t) image->columns; x != 0; x--)
4870     {
4871       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4872       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4873       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4874       q+=GetPixelChannels(image);
4875       s+=GetPixelChannels(jng_image);
4876     }
4877
4878     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4879       break;
4880   }
4881
4882   jng_image=DestroyImage(jng_image);
4883
4884   if (image_info->ping == MagickFalse)
4885     {
4886      if (jng_color_type >= 12)
4887        {
4888          if (jng_alpha_compression_method == 0)
4889            {
4890              png_byte
4891                data[5];
4892              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4893              PNGType(data,mng_IEND);
4894              LogPNGChunk(logging,mng_IEND,0L);
4895              (void) WriteBlob(alpha_image,4,data);
4896              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4897            }
4898
4899          (void) CloseBlob(alpha_image);
4900
4901          if (logging != MagickFalse)
4902            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4903              "    Reading alpha from alpha_blob.");
4904
4905          (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
4906            "%s",alpha_image->filename);
4907
4908          jng_image=ReadImage(alpha_image_info,exception);
4909
4910          if (jng_image != (Image *) NULL)
4911            for (y=0; y < (ssize_t) image->rows; y++)
4912            {
4913              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4914                exception);
4915              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4916
4917              if (image->alpha_trait != UndefinedPixelTrait)
4918                for (x=(ssize_t) image->columns; x != 0; x--)
4919                {
4920                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4921                   q+=GetPixelChannels(image);
4922                   s+=GetPixelChannels(jng_image);
4923                }
4924
4925              else
4926                for (x=(ssize_t) image->columns; x != 0; x--)
4927                {
4928                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4929                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4930                     image->alpha_trait=BlendPixelTrait;
4931                   q+=GetPixelChannels(image);
4932                   s+=GetPixelChannels(jng_image);
4933                }
4934
4935              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4936                break;
4937            }
4938          (void) RelinquishUniqueFileResource(alpha_image->filename);
4939          alpha_image=DestroyImage(alpha_image);
4940          alpha_image_info=DestroyImageInfo(alpha_image_info);
4941          if (jng_image != (Image *) NULL)
4942            jng_image=DestroyImage(jng_image);
4943        }
4944     }
4945
4946   /* Read the JNG image.  */
4947
4948   if (mng_info->mng_type == 0)
4949     {
4950       mng_info->mng_width=jng_width;
4951       mng_info->mng_height=jng_height;
4952     }
4953
4954   if (image->page.width == 0 && image->page.height == 0)
4955     {
4956       image->page.width=jng_width;
4957       image->page.height=jng_height;
4958     }
4959
4960   if (image->page.x == 0 && image->page.y == 0)
4961     {
4962       image->page.x=mng_info->x_off[mng_info->object_id];
4963       image->page.y=mng_info->y_off[mng_info->object_id];
4964     }
4965
4966   else
4967     {
4968       image->page.y=mng_info->y_off[mng_info->object_id];
4969     }
4970
4971   mng_info->image_found++;
4972   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4973     2*GetBlobSize(image));
4974
4975   if (status == MagickFalse)
4976     return(DestroyImageList(image));
4977
4978   if (logging != MagickFalse)
4979     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4980       "  exit ReadOneJNGImage()");
4981
4982   return(image);
4983 }
4984
4985 /*
4986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4987 %                                                                             %
4988 %                                                                             %
4989 %                                                                             %
4990 %   R e a d J N G I m a g e                                                   %
4991 %                                                                             %
4992 %                                                                             %
4993 %                                                                             %
4994 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4995 %
4996 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4997 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4998 %  necessary for the new Image structure and returns a pointer to the new
4999 %  image.
5000 %
5001 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
5002 %
5003 %  The format of the ReadJNGImage method is:
5004 %
5005 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
5006 %         *exception)
5007 %
5008 %  A description of each parameter follows:
5009 %
5010 %    o image_info: the image info.
5011 %
5012 %    o exception: return any errors or warnings in this structure.
5013 %
5014 */
5015
5016 static Image *ReadJNGImage(const ImageInfo *image_info,
5017                 ExceptionInfo *exception)
5018 {
5019   Image
5020     *image;
5021
5022   MagickBooleanType
5023     logging,
5024     status;
5025
5026   MngInfo
5027     *mng_info;
5028
5029   char
5030     magic_number[MagickPathExtent];
5031
5032   size_t
5033     count;
5034
5035   /*
5036     Open image file.
5037   */
5038   assert(image_info != (const ImageInfo *) NULL);
5039   assert(image_info->signature == MagickCoreSignature);
5040   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
5041      image_info->filename);
5042   assert(exception != (ExceptionInfo *) NULL);
5043   assert(exception->signature == MagickCoreSignature);
5044   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
5045   image=AcquireImage(image_info,exception);
5046   mng_info=(MngInfo *) NULL;
5047   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5048
5049   if (status == MagickFalse)
5050     return((Image *) NULL);
5051
5052   if (LocaleCompare(image_info->magick,"JNG") != 0)
5053     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5054
5055   /* Verify JNG signature.  */
5056
5057   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5058
5059   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
5060     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5061
5062   /*
5063      Verify that file size large enough to contain a JNG datastream.
5064   */
5065   if (GetBlobSize(image) < 147)
5066     ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
5067
5068   /* Allocate a MngInfo structure.  */
5069
5070   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
5071
5072   if (mng_info == (MngInfo *) NULL)
5073     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5074
5075   /* Initialize members of the MngInfo structure.  */
5076
5077   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5078
5079   mng_info->image=image;
5080   image=ReadOneJNGImage(mng_info,image_info,exception);
5081   mng_info=MngInfoFreeStruct(mng_info);
5082
5083   if (image == (Image *) NULL)
5084     {
5085       if (logging != MagickFalse)
5086         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5087           "exit ReadJNGImage() with error");
5088
5089       return((Image *) NULL);
5090     }
5091   (void) CloseBlob(image);
5092
5093   if (image->columns == 0 || image->rows == 0)
5094     {
5095       if (logging != MagickFalse)
5096         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5097           "exit ReadJNGImage() with error");
5098
5099       ThrowReaderException(CorruptImageError,"CorruptImage");
5100     }
5101
5102   if (logging != MagickFalse)
5103     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
5104
5105   return(image);
5106 }
5107 #endif
5108
5109 static Image *ReadOneMNGImage(MngInfo* mng_info, const ImageInfo *image_info,
5110      ExceptionInfo *exception)
5111 {
5112   char
5113     page_geometry[MagickPathExtent];
5114
5115   Image
5116     *image;
5117
5118   MagickBooleanType
5119     logging;
5120
5121   volatile int
5122     first_mng_object,
5123     object_id,
5124     term_chunk_found,
5125     skip_to_iend;
5126
5127   volatile ssize_t
5128     image_count=0;
5129
5130   MagickBooleanType
5131     status;
5132
5133   MagickOffsetType
5134     offset;
5135
5136   MngBox
5137     default_fb,
5138     fb,
5139     previous_fb;
5140
5141 #if defined(MNG_INSERT_LAYERS)
5142   PixelInfo
5143     mng_background_color;
5144 #endif
5145
5146   register unsigned char
5147     *p;
5148
5149   register ssize_t
5150     i;
5151
5152   size_t
5153     count;
5154
5155   ssize_t
5156     loop_level;
5157
5158   volatile short
5159     skipping_loop;
5160
5161 #if defined(MNG_INSERT_LAYERS)
5162   unsigned int
5163     mandatory_back=0;
5164 #endif
5165
5166   volatile unsigned int
5167 #ifdef MNG_OBJECT_BUFFERS
5168     mng_background_object=0,
5169 #endif
5170     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
5171
5172   size_t
5173     default_frame_timeout,
5174     frame_timeout,
5175 #if defined(MNG_INSERT_LAYERS)
5176     image_height,
5177     image_width,
5178 #endif
5179     length;
5180
5181   /* These delays are all measured in image ticks_per_second,
5182    * not in MNG ticks_per_second
5183    */
5184   volatile size_t
5185     default_frame_delay,
5186     final_delay,
5187     final_image_delay,
5188     frame_delay,
5189 #if defined(MNG_INSERT_LAYERS)
5190     insert_layers,
5191 #endif
5192     mng_iterations=1,
5193     simplicity=0,
5194     subframe_height=0,
5195     subframe_width=0;
5196
5197   previous_fb.top=0;
5198   previous_fb.bottom=0;
5199   previous_fb.left=0;
5200   previous_fb.right=0;
5201   default_fb.top=0;
5202   default_fb.bottom=0;
5203   default_fb.left=0;
5204   default_fb.right=0;
5205
5206   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
5207     "  Enter ReadOneMNGImage()");
5208
5209   image=mng_info->image;
5210
5211   if (LocaleCompare(image_info->magick,"MNG") == 0)
5212     {
5213       char
5214         magic_number[MagickPathExtent];
5215
5216       /* Verify MNG signature.  */
5217       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5218       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5219         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5220
5221       /* Initialize some nonzero members of the MngInfo structure.  */
5222       for (i=0; i < MNG_MAX_OBJECTS; i++)
5223       {
5224         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5225         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5226       }
5227       mng_info->exists[0]=MagickTrue;
5228     }
5229
5230   skipping_loop=(-1);
5231   first_mng_object=MagickTrue;
5232   mng_type=0;
5233 #if defined(MNG_INSERT_LAYERS)
5234   insert_layers=MagickFalse; /* should be False during convert or mogrify */
5235 #endif
5236   default_frame_delay=0;
5237   default_frame_timeout=0;
5238   frame_delay=0;
5239   final_delay=1;
5240   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5241   object_id=0;
5242   skip_to_iend=MagickFalse;
5243   term_chunk_found=MagickFalse;
5244   mng_info->framing_mode=1;
5245 #if defined(MNG_INSERT_LAYERS)
5246   mandatory_back=MagickFalse;
5247 #endif
5248 #if defined(MNG_INSERT_LAYERS)
5249   mng_background_color=image->background_color;
5250 #endif
5251   default_fb=mng_info->frame;
5252   previous_fb=mng_info->frame;
5253   do
5254   {
5255     char
5256       type[MagickPathExtent];
5257
5258     if (LocaleCompare(image_info->magick,"MNG") == 0)
5259       {
5260         unsigned char
5261           *chunk;
5262
5263         /*
5264           Read a new chunk.
5265         */
5266         type[0]='\0';
5267         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5268         length=ReadBlobMSBLong(image);
5269         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5270
5271         if (logging != MagickFalse)
5272           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5273            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5274            type[0],type[1],type[2],type[3],(double) length);
5275
5276         if (length > PNG_UINT_31_MAX)
5277           {
5278             status=MagickFalse;
5279             break;
5280           }
5281
5282         if (count == 0)
5283           ThrowReaderException(CorruptImageError,"CorruptImage");
5284
5285         p=NULL;
5286         chunk=(unsigned char *) NULL;
5287
5288         if (length != 0)
5289           {
5290             if (length > GetBlobSize(image))
5291               ThrowReaderException(CorruptImageError,
5292                 "InsufficientImageDataInFile");
5293             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5294
5295             if (chunk == (unsigned char *) NULL)
5296               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5297
5298             for (i=0; i < (ssize_t) length; i++)
5299             {
5300               int
5301                 c;
5302
5303               c=ReadBlobByte(image);
5304               if (c == EOF)
5305                 break;
5306               chunk[i]=(unsigned char) c;
5307             }
5308
5309             p=chunk;
5310           }
5311
5312         (void) ReadBlobMSBLong(image);  /* read crc word */
5313
5314 #if !defined(JNG_SUPPORTED)
5315         if (memcmp(type,mng_JHDR,4) == 0)
5316           {
5317             skip_to_iend=MagickTrue;
5318
5319             if (mng_info->jhdr_warning == 0)
5320               (void) ThrowMagickException(exception,GetMagickModule(),
5321                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5322
5323             mng_info->jhdr_warning++;
5324           }
5325 #endif
5326         if (memcmp(type,mng_DHDR,4) == 0)
5327           {
5328             skip_to_iend=MagickTrue;
5329
5330             if (mng_info->dhdr_warning == 0)
5331               (void) ThrowMagickException(exception,GetMagickModule(),
5332                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5333
5334             mng_info->dhdr_warning++;
5335           }
5336         if (memcmp(type,mng_MEND,4) == 0)
5337           {
5338             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5339             break;
5340           }
5341
5342         if (skip_to_iend)
5343           {
5344             if (memcmp(type,mng_IEND,4) == 0)
5345               skip_to_iend=MagickFalse;
5346
5347             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5348
5349             if (logging != MagickFalse)
5350               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5351                 "  Skip to IEND.");
5352
5353             continue;
5354           }
5355
5356         if (memcmp(type,mng_MHDR,4) == 0)
5357           {
5358             if (length != 28)
5359               {
5360                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5361                 ThrowReaderException(CorruptImageError,"CorruptImage");
5362               }
5363
5364             mng_info->mng_width=(unsigned long)mng_get_long(p);
5365             mng_info->mng_height=(unsigned long)mng_get_long(&p[4]);
5366
5367             if (logging != MagickFalse)
5368               {
5369                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5370                   "  MNG width: %.20g",(double) mng_info->mng_width);
5371                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5372                   "  MNG height: %.20g",(double) mng_info->mng_height);
5373               }
5374
5375             p+=8;
5376             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5377
5378             if (mng_info->ticks_per_second == 0)
5379               default_frame_delay=0;
5380
5381             else
5382               default_frame_delay=1UL*image->ticks_per_second/
5383                 mng_info->ticks_per_second;
5384
5385             frame_delay=default_frame_delay;
5386             simplicity=0;
5387
5388             p+=16;
5389             simplicity=(size_t) mng_get_long(p);
5390
5391             mng_type=1;    /* Full MNG */
5392
5393             if ((simplicity != 0) && ((simplicity | 11) == 11))
5394               mng_type=2; /* LC */
5395
5396             if ((simplicity != 0) && ((simplicity | 9) == 9))
5397               mng_type=3; /* VLC */
5398
5399 #if defined(MNG_INSERT_LAYERS)
5400             if (mng_type != 3)
5401               insert_layers=MagickTrue;
5402 #endif
5403             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5404               {
5405                 /* Allocate next image structure.  */
5406                 AcquireNextImage(image_info,image,exception);
5407
5408                 if (GetNextImageInList(image) == (Image *) NULL)
5409                   return((Image *) NULL);
5410
5411                 image=SyncNextImageInList(image);
5412                 mng_info->image=image;
5413               }
5414
5415             if ((mng_info->mng_width > 65535L) ||
5416                 (mng_info->mng_height > 65535L))
5417               {
5418                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5419                 ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5420               }
5421
5422             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5423               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5424               mng_info->mng_height);
5425
5426             mng_info->frame.left=0;
5427             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5428             mng_info->frame.top=0;
5429             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5430             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5431
5432             for (i=0; i < MNG_MAX_OBJECTS; i++)
5433               mng_info->object_clip[i]=mng_info->frame;
5434
5435             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5436             continue;
5437           }
5438
5439         if (memcmp(type,mng_TERM,4) == 0)
5440           {
5441             int
5442               repeat=0;
5443
5444             if (length != 0)
5445               repeat=p[0];
5446
5447             if (repeat == 3 && length > 8)
5448               {
5449                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5450                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5451
5452                 if (mng_iterations == PNG_UINT_31_MAX)
5453                   mng_iterations=0;
5454
5455                 image->iterations=mng_iterations;
5456                 term_chunk_found=MagickTrue;
5457               }
5458
5459             if (logging != MagickFalse)
5460               {
5461                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5462                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5463                   repeat,(double) final_delay, (double) image->iterations);
5464               }
5465
5466             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5467             continue;
5468           }
5469         if (memcmp(type,mng_DEFI,4) == 0)
5470           {
5471             if (mng_type == 3)
5472               (void) ThrowMagickException(exception,GetMagickModule(),
5473                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5474                 image->filename);
5475
5476             if (length < 2)
5477               {
5478                 chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5479                 ThrowReaderException(CorruptImageError,"CorruptImage");
5480               }
5481
5482             object_id=(p[0] << 8) | p[1];
5483
5484             if (mng_type == 2 && object_id != 0)
5485               (void) ThrowMagickException(exception,GetMagickModule(),
5486                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5487                 image->filename);
5488
5489             if (object_id > MNG_MAX_OBJECTS)
5490               {
5491                 /*
5492                   Instead of using a warning we should allocate a larger
5493                   MngInfo structure and continue.
5494                 */
5495                 (void) ThrowMagickException(exception,GetMagickModule(),
5496                   CoderError,"object id too large","`%s'",image->filename);
5497                 object_id=MNG_MAX_OBJECTS;
5498               }
5499
5500             if (mng_info->exists[object_id])
5501               if (mng_info->frozen[object_id])
5502                 {
5503                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5504                   (void) ThrowMagickException(exception,
5505                     GetMagickModule(),CoderError,
5506                     "DEFI cannot redefine a frozen MNG object","`%s'",
5507                     image->filename);
5508                   continue;
5509                 }
5510
5511             mng_info->exists[object_id]=MagickTrue;
5512
5513             if (length > 2)
5514               mng_info->invisible[object_id]=p[2];
5515
5516             /*
5517               Extract object offset info.
5518             */
5519             if (length > 11)
5520               {
5521                 mng_info->x_off[object_id]=(ssize_t) mng_get_long(&p[4]);
5522                 mng_info->y_off[object_id]=(ssize_t) mng_get_long(&p[8]);
5523                 if (logging != MagickFalse)
5524                   {
5525                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5526                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5527                       object_id,(double) mng_info->x_off[object_id],
5528                       object_id,(double) mng_info->y_off[object_id]);
5529                   }
5530               }
5531
5532             /*
5533               Extract object clipping info.
5534             */
5535             if (length > 27)
5536               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5537                 &p[12]);
5538
5539             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5540             continue;
5541           }
5542         if (memcmp(type,mng_bKGD,4) == 0)
5543           {
5544             mng_info->have_global_bkgd=MagickFalse;
5545
5546             if (length > 5)
5547               {
5548                 mng_info->mng_global_bkgd.red=
5549                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5550
5551                 mng_info->mng_global_bkgd.green=
5552                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5553
5554                 mng_info->mng_global_bkgd.blue=
5555                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5556
5557                 mng_info->have_global_bkgd=MagickTrue;
5558               }
5559
5560             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5561             continue;
5562           }
5563         if (memcmp(type,mng_BACK,4) == 0)
5564           {
5565 #if defined(MNG_INSERT_LAYERS)
5566             if (length > 6)
5567               mandatory_back=p[6];
5568
5569             else
5570               mandatory_back=0;
5571
5572             if (mandatory_back && length > 5)
5573               {
5574                 mng_background_color.red=
5575                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5576
5577                 mng_background_color.green=
5578                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5579
5580                 mng_background_color.blue=
5581                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5582
5583                 mng_background_color.alpha=OpaqueAlpha;
5584               }
5585
5586 #ifdef MNG_OBJECT_BUFFERS
5587             if (length > 8)
5588               mng_background_object=(p[7] << 8) | p[8];
5589 #endif
5590 #endif
5591             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5592             continue;
5593           }
5594
5595         if (memcmp(type,mng_PLTE,4) == 0)
5596           {
5597             /* Read global PLTE.  */
5598
5599             if (length && (length < 769))
5600               {
5601                 if (mng_info->global_plte == (png_colorp) NULL)
5602                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5603                     sizeof(*mng_info->global_plte));
5604
5605                 for (i=0; i < (ssize_t) (length/3); i++)
5606                 {
5607                   mng_info->global_plte[i].red=p[3*i];
5608                   mng_info->global_plte[i].green=p[3*i+1];
5609                   mng_info->global_plte[i].blue=p[3*i+2];
5610                 }
5611
5612                 mng_info->global_plte_length=(unsigned int) (length/3);
5613               }
5614 #ifdef MNG_LOOSE
5615             for ( ; i < 256; i++)
5616             {
5617               mng_info->global_plte[i].red=i;
5618               mng_info->global_plte[i].green=i;
5619               mng_info->global_plte[i].blue=i;
5620             }
5621
5622             if (length != 0)
5623               mng_info->global_plte_length=256;
5624 #endif
5625             else
5626               mng_info->global_plte_length=0;
5627
5628             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5629             continue;
5630           }
5631
5632         if (memcmp(type,mng_tRNS,4) == 0)
5633           {
5634             /* read global tRNS */
5635
5636             if (length > 0 && length < 257)
5637               for (i=0; i < (ssize_t) length; i++)
5638                 mng_info->global_trns[i]=p[i];
5639
5640 #ifdef MNG_LOOSE
5641             for ( ; i < 256; i++)
5642               mng_info->global_trns[i]=255;
5643 #endif
5644             mng_info->global_trns_length=(unsigned int) length;
5645             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5646             continue;
5647           }
5648         if (memcmp(type,mng_gAMA,4) == 0)
5649           {
5650             if (length == 4)
5651               {
5652                 ssize_t
5653                   igamma;
5654
5655                 igamma=mng_get_long(p);
5656                 mng_info->global_gamma=((float) igamma)*0.00001;
5657                 mng_info->have_global_gama=MagickTrue;
5658               }
5659
5660             else
5661               mng_info->have_global_gama=MagickFalse;
5662
5663             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5664             continue;
5665           }
5666
5667         if (memcmp(type,mng_cHRM,4) == 0)
5668           {
5669             /* Read global cHRM */
5670
5671             if (length == 32)
5672               {
5673                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5674                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5675                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5676                 mng_info->global_chrm.red_primary.y=0.00001*
5677                   mng_get_long(&p[12]);
5678                 mng_info->global_chrm.green_primary.x=0.00001*
5679                   mng_get_long(&p[16]);
5680                 mng_info->global_chrm.green_primary.y=0.00001*
5681                   mng_get_long(&p[20]);
5682                 mng_info->global_chrm.blue_primary.x=0.00001*
5683                   mng_get_long(&p[24]);
5684                 mng_info->global_chrm.blue_primary.y=0.00001*
5685                   mng_get_long(&p[28]);
5686                 mng_info->have_global_chrm=MagickTrue;
5687               }
5688             else
5689               mng_info->have_global_chrm=MagickFalse;
5690
5691             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5692             continue;
5693           }
5694
5695         if (memcmp(type,mng_sRGB,4) == 0)
5696           {
5697             /*
5698               Read global sRGB.
5699             */
5700             if (length != 0)
5701               {
5702                 mng_info->global_srgb_intent=
5703                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5704                 mng_info->have_global_srgb=MagickTrue;
5705               }
5706             else
5707               mng_info->have_global_srgb=MagickFalse;
5708
5709             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5710             continue;
5711           }
5712
5713         if (memcmp(type,mng_iCCP,4) == 0)
5714           {
5715             /* To do: */
5716
5717             /*
5718               Read global iCCP.
5719             */
5720             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5721
5722             continue;
5723           }
5724
5725         if (memcmp(type,mng_FRAM,4) == 0)
5726           {
5727             if (mng_type == 3)
5728               (void) ThrowMagickException(exception,GetMagickModule(),
5729                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5730                 image->filename);
5731
5732             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5733               image->delay=frame_delay;
5734
5735             frame_delay=default_frame_delay;
5736             frame_timeout=default_frame_timeout;
5737             fb=default_fb;
5738
5739             if (length != 0)
5740               if (p[0])
5741                 mng_info->framing_mode=p[0];
5742
5743             if (logging != MagickFalse)
5744               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5745                 "    Framing_mode=%d",mng_info->framing_mode);
5746
5747             if (length > 6)
5748               {
5749                 /* Note the delay and frame clipping boundaries.  */
5750
5751                 p++; /* framing mode */
5752
5753                 while (*p && ((p-chunk) < (ssize_t) length))
5754                   p++;  /* frame name */
5755
5756                 p++;  /* frame name terminator */
5757
5758                 if ((p-chunk) < (ssize_t) (length-4))
5759                   {
5760                     int
5761                       change_delay,
5762                       change_timeout,
5763                       change_clipping;
5764
5765                     change_delay=(*p++);
5766                     change_timeout=(*p++);
5767                     change_clipping=(*p++);
5768                     p++; /* change_sync */
5769
5770                     if (change_delay && ((p-chunk) < (ssize_t) (length-4)))
5771                       {
5772                         frame_delay=1UL*image->ticks_per_second*
5773                           mng_get_long(p);
5774
5775                         if (mng_info->ticks_per_second != 0)
5776                           frame_delay/=mng_info->ticks_per_second;
5777
5778                         else
5779                           frame_delay=PNG_UINT_31_MAX;
5780
5781                         if (change_delay == 2)
5782                           default_frame_delay=frame_delay;
5783
5784                         p+=4;
5785
5786                         if (logging != MagickFalse)
5787                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5788                             "    Framing_delay=%.20g",(double) frame_delay);
5789                       }
5790
5791                     if (change_timeout && ((p-chunk) < (ssize_t) (length-4)))
5792                       {
5793                         frame_timeout=1UL*image->ticks_per_second*
5794                           mng_get_long(p);
5795
5796                         if (mng_info->ticks_per_second != 0)
5797                           frame_timeout/=mng_info->ticks_per_second;
5798
5799                         else
5800                           frame_timeout=PNG_UINT_31_MAX;
5801
5802                         if (change_timeout == 2)
5803                           default_frame_timeout=frame_timeout;
5804
5805                         p+=4;
5806
5807                         if (logging != MagickFalse)
5808                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5809                             "    Framing_timeout=%.20g",(double) frame_timeout);
5810                       }
5811
5812                     if (change_clipping && ((p-chunk) < (ssize_t) (length-16)))
5813                       {
5814                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5815                         p+=16;
5816                         previous_fb=fb;
5817
5818                         if (logging != MagickFalse)
5819                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5820                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5821                             (double) fb.left,(double) fb.right,(double) fb.top,
5822                             (double) fb.bottom);
5823
5824                         if (change_clipping == 2)
5825                           default_fb=fb;
5826                       }
5827                   }
5828               }
5829             mng_info->clip=fb;
5830             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5831
5832             subframe_width=(size_t) (mng_info->clip.right
5833                -mng_info->clip.left);
5834
5835             subframe_height=(size_t) (mng_info->clip.bottom
5836                -mng_info->clip.top);
5837             /*
5838               Insert a background layer behind the frame if framing_mode is 4.
5839             */
5840 #if defined(MNG_INSERT_LAYERS)
5841             if (logging != MagickFalse)
5842               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5843                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5844                 subframe_width,(double) subframe_height);
5845
5846             if (insert_layers && (mng_info->framing_mode == 4) &&
5847                 (subframe_width) && (subframe_height))
5848               {
5849                 /* Allocate next image structure.  */
5850                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5851                   {
5852                     AcquireNextImage(image_info,image,exception);
5853
5854                     if (GetNextImageInList(image) == (Image *) NULL)
5855                       return(DestroyImageList(image));
5856
5857                     image=SyncNextImageInList(image);
5858                   }
5859
5860                 mng_info->image=image;
5861
5862                 if (term_chunk_found)
5863                   {
5864                     image->start_loop=MagickTrue;
5865                     image->iterations=mng_iterations;
5866                     term_chunk_found=MagickFalse;
5867                   }
5868
5869                 else
5870                     image->start_loop=MagickFalse;
5871
5872                 image->columns=subframe_width;
5873                 image->rows=subframe_height;
5874                 image->page.width=subframe_width;
5875                 image->page.height=subframe_height;
5876                 image->page.x=mng_info->clip.left;
5877                 image->page.y=mng_info->clip.top;
5878                 image->background_color=mng_background_color;
5879                 image->alpha_trait=UndefinedPixelTrait;
5880                 image->delay=0;
5881                 (void) SetImageBackgroundColor(image,exception);
5882
5883                 if (logging != MagickFalse)
5884                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5885                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5886                     (double) mng_info->clip.left,
5887                     (double) mng_info->clip.right,
5888                     (double) mng_info->clip.top,
5889                     (double) mng_info->clip.bottom);
5890               }
5891 #endif
5892             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5893             continue;
5894           }
5895
5896         if (memcmp(type,mng_CLIP,4) == 0)
5897           {
5898             unsigned int
5899               first_object,
5900               last_object;
5901
5902             /*
5903               Read CLIP.
5904             */
5905             if (length > 3)
5906               {
5907                 first_object=(p[0] << 8) | p[1];
5908                 last_object=(p[2] << 8) | p[3];
5909                 p+=4;
5910
5911                 for (i=(int) first_object; i <= (int) last_object; i++)
5912                 {
5913                   if ((i < 0) || (i >= MNG_MAX_OBJECTS))
5914                     continue;
5915
5916                   if (mng_info->exists[i] && !mng_info->frozen[i])
5917                     {
5918                       MngBox
5919                         box;
5920
5921                       box=mng_info->object_clip[i];
5922                       if ((p-chunk) < (ssize_t) (length-17))
5923                         mng_info->object_clip[i]=
5924                            mng_read_box(box,(char) p[0],&p[1]);
5925                     }
5926                 }
5927
5928               }
5929             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5930             continue;
5931           }
5932
5933         if (memcmp(type,mng_SAVE,4) == 0)
5934           {
5935             for (i=1; i < MNG_MAX_OBJECTS; i++)
5936               if (mng_info->exists[i])
5937                 {
5938                  mng_info->frozen[i]=MagickTrue;
5939 #ifdef MNG_OBJECT_BUFFERS
5940                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5941                     mng_info->ob[i]->frozen=MagickTrue;
5942 #endif
5943                 }
5944
5945             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5946
5947             continue;
5948           }
5949
5950         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5951           {
5952             /* Read DISC or SEEK.  */
5953
5954             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5955               {
5956                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5957                   MngInfoDiscardObject(mng_info,i);
5958               }
5959
5960             else
5961               {
5962                 register ssize_t
5963                   j;
5964
5965                 for (j=1; j < (ssize_t) length; j+=2)
5966                 {
5967                   i=p[j-1] << 8 | p[j];
5968                   MngInfoDiscardObject(mng_info,i);
5969                 }
5970               }
5971
5972             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5973
5974             continue;
5975           }
5976
5977         if (memcmp(type,mng_MOVE,4) == 0)
5978           {
5979             size_t
5980               first_object,
5981               last_object;
5982
5983             /* read MOVE */
5984
5985             if (length > 3)
5986             {
5987               first_object=(p[0] << 8) | p[1];
5988               last_object=(p[2] << 8) | p[3];
5989               p+=4;
5990
5991               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5992               {
5993                 if ((i < 0) || (i >= MNG_MAX_OBJECTS))
5994                   continue;
5995
5996                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5997                     (p-chunk) < (ssize_t) (length-8))
5998                   {
5999                     MngPair
6000                       new_pair;
6001
6002                     MngPair
6003                       old_pair;
6004
6005                     old_pair.a=mng_info->x_off[i];
6006                     old_pair.b=mng_info->y_off[i];
6007                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
6008                     mng_info->x_off[i]=new_pair.a;
6009                     mng_info->y_off[i]=new_pair.b;
6010                   }
6011               }
6012             }
6013
6014             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6015             continue;
6016           }
6017
6018         if (memcmp(type,mng_LOOP,4) == 0)
6019           {
6020             ssize_t loop_iters=1;
6021             if (length > 4)
6022               {
6023                 loop_level=chunk[0];
6024                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
6025
6026                 /* Record starting point.  */
6027                 loop_iters=mng_get_long(&chunk[1]);
6028
6029                 if (logging != MagickFalse)
6030                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6031                     "  LOOP level %.20g has %.20g iterations ",
6032                     (double) loop_level, (double) loop_iters);
6033
6034                 if (loop_iters == 0)
6035                   skipping_loop=loop_level;
6036
6037                 else
6038                   {
6039                     mng_info->loop_jump[loop_level]=TellBlob(image);
6040                     mng_info->loop_count[loop_level]=loop_iters;
6041                   }
6042
6043                 mng_info->loop_iteration[loop_level]=0;
6044               }
6045             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6046             continue;
6047           }
6048
6049         if (memcmp(type,mng_ENDL,4) == 0)
6050           {
6051             if (length > 0)
6052               {
6053                 loop_level=chunk[0];
6054
6055                 if (skipping_loop > 0)
6056                   {
6057                     if (skipping_loop == loop_level)
6058                       {
6059                         /*
6060                           Found end of zero-iteration loop.
6061                         */
6062                         skipping_loop=(-1);
6063                         mng_info->loop_active[loop_level]=0;
6064                       }
6065                   }
6066
6067                 else
6068                   {
6069                     if (mng_info->loop_active[loop_level] == 1)
6070                       {
6071                         mng_info->loop_count[loop_level]--;
6072                         mng_info->loop_iteration[loop_level]++;
6073
6074                         if (logging != MagickFalse)
6075                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6076                           "  ENDL: LOOP level %.20g has %.20g remaining iters",
6077                             (double) loop_level,(double)
6078                             mng_info->loop_count[loop_level]);
6079
6080                         if (mng_info->loop_count[loop_level] != 0)
6081                           {
6082                             offset=
6083                               SeekBlob(image,mng_info->loop_jump[loop_level],
6084                               SEEK_SET);
6085
6086                             if (offset < 0)
6087                               {
6088                                 chunk=(unsigned char *) RelinquishMagickMemory(
6089                                   chunk);
6090                                 ThrowReaderException(CorruptImageError,
6091                                   "ImproperImageHeader");
6092                               }
6093                           }
6094
6095                         else
6096                           {
6097                             short
6098                               last_level;
6099
6100                             /*
6101                               Finished loop.
6102                             */
6103                             mng_info->loop_active[loop_level]=0;
6104                             last_level=(-1);
6105                             for (i=0; i < loop_level; i++)
6106                               if (mng_info->loop_active[i] == 1)
6107                                 last_level=(short) i;
6108                             loop_level=last_level;
6109                           }
6110                       }
6111                   }
6112               }
6113
6114             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6115             continue;
6116           }
6117
6118         if (memcmp(type,mng_CLON,4) == 0)
6119           {
6120             if (mng_info->clon_warning == 0)
6121               (void) ThrowMagickException(exception,GetMagickModule(),
6122                 CoderError,"CLON is not implemented yet","`%s'",
6123                 image->filename);
6124
6125             mng_info->clon_warning++;
6126           }
6127
6128         if (memcmp(type,mng_MAGN,4) == 0)
6129           {
6130             png_uint_16
6131               magn_first,
6132               magn_last,
6133               magn_mb,
6134               magn_ml,
6135               magn_mr,
6136               magn_mt,
6137               magn_mx,
6138               magn_my,
6139               magn_methx,
6140               magn_methy;
6141
6142             if (length > 1)
6143               magn_first=(p[0] << 8) | p[1];
6144
6145             else
6146               magn_first=0;
6147
6148             if (length > 3)
6149               magn_last=(p[2] << 8) | p[3];
6150
6151             else
6152               magn_last=magn_first;
6153 #ifndef MNG_OBJECT_BUFFERS
6154             if (magn_first || magn_last)
6155               if (mng_info->magn_warning == 0)
6156                 {
6157                   (void) ThrowMagickException(exception,
6158                      GetMagickModule(),CoderError,
6159                      "MAGN is not implemented yet for nonzero objects",
6160                      "`%s'",image->filename);
6161
6162                    mng_info->magn_warning++;
6163                 }
6164 #endif
6165             if (length > 4)
6166               magn_methx=p[4];
6167
6168             else
6169               magn_methx=0;
6170
6171             if (length > 6)
6172               magn_mx=(p[5] << 8) | p[6];
6173
6174             else
6175               magn_mx=1;
6176
6177             if (magn_mx == 0)
6178               magn_mx=1;
6179
6180             if (length > 8)
6181               magn_my=(p[7] << 8) | p[8];
6182
6183             else
6184               magn_my=magn_mx;
6185
6186             if (magn_my == 0)
6187               magn_my=1;
6188
6189             if (length > 10)
6190               magn_ml=(p[9] << 8) | p[10];
6191
6192             else
6193               magn_ml=magn_mx;
6194
6195             if (magn_ml == 0)
6196               magn_ml=1;
6197
6198             if (length > 12)
6199               magn_mr=(p[11] << 8) | p[12];
6200
6201             else
6202               magn_mr=magn_mx;
6203
6204             if (magn_mr == 0)
6205               magn_mr=1;
6206
6207             if (length > 14)
6208               magn_mt=(p[13] << 8) | p[14];
6209
6210             else
6211               magn_mt=magn_my;
6212
6213             if (magn_mt == 0)
6214               magn_mt=1;
6215
6216             if (length > 16)
6217               magn_mb=(p[15] << 8) | p[16];
6218
6219             else
6220               magn_mb=magn_my;
6221
6222             if (magn_mb == 0)
6223               magn_mb=1;
6224
6225             if (length > 17)
6226               magn_methy=p[17];
6227
6228             else
6229               magn_methy=magn_methx;
6230
6231
6232             if (magn_methx > 5 || magn_methy > 5)
6233               if (mng_info->magn_warning == 0)
6234                 {
6235                   (void) ThrowMagickException(exception,
6236                      GetMagickModule(),CoderError,
6237                      "Unknown MAGN method in MNG datastream","`%s'",
6238                      image->filename);
6239
6240                    mng_info->magn_warning++;
6241                 }
6242 #ifdef MNG_OBJECT_BUFFERS
6243           /* Magnify existing objects in the range magn_first to magn_last */
6244 #endif
6245             if (magn_first == 0 || magn_last == 0)
6246               {
6247                 /* Save the magnification factors for object 0 */
6248                 mng_info->magn_mb=magn_mb;
6249                 mng_info->magn_ml=magn_ml;
6250                 mng_info->magn_mr=magn_mr;
6251                 mng_info->magn_mt=magn_mt;
6252                 mng_info->magn_mx=magn_mx;
6253                 mng_info->magn_my=magn_my;
6254                 mng_info->magn_methx=magn_methx;
6255                 mng_info->magn_methy=magn_methy;
6256               }
6257           }
6258
6259         if (memcmp(type,mng_PAST,4) == 0)
6260           {
6261             if (mng_info->past_warning == 0)
6262               (void) ThrowMagickException(exception,GetMagickModule(),
6263                 CoderError,"PAST is not implemented yet","`%s'",
6264                 image->filename);
6265
6266             mng_info->past_warning++;
6267           }
6268
6269         if (memcmp(type,mng_SHOW,4) == 0)
6270           {
6271             if (mng_info->show_warning == 0)
6272               (void) ThrowMagickException(exception,GetMagickModule(),
6273                 CoderError,"SHOW is not implemented yet","`%s'",
6274                 image->filename);
6275
6276             mng_info->show_warning++;
6277           }
6278
6279         if (memcmp(type,mng_sBIT,4) == 0)
6280           {
6281             if (length < 4)
6282               mng_info->have_global_sbit=MagickFalse;
6283
6284             else
6285               {
6286                 mng_info->global_sbit.gray=p[0];
6287                 mng_info->global_sbit.red=p[0];
6288                 mng_info->global_sbit.green=p[1];
6289                 mng_info->global_sbit.blue=p[2];
6290                 mng_info->global_sbit.alpha=p[3];
6291                 mng_info->have_global_sbit=MagickTrue;
6292              }
6293           }
6294         if (memcmp(type,mng_pHYs,4) == 0)
6295           {
6296             if (length > 8)
6297               {
6298                 mng_info->global_x_pixels_per_unit=
6299                     (size_t) mng_get_long(p);
6300                 mng_info->global_y_pixels_per_unit=
6301                     (size_t) mng_get_long(&p[4]);
6302                 mng_info->global_phys_unit_type=p[8];
6303                 mng_info->have_global_phys=MagickTrue;
6304               }
6305
6306             else
6307               mng_info->have_global_phys=MagickFalse;
6308           }
6309         if (memcmp(type,mng_pHYg,4) == 0)
6310           {
6311             if (mng_info->phyg_warning == 0)
6312               (void) ThrowMagickException(exception,GetMagickModule(),
6313                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6314
6315             mng_info->phyg_warning++;
6316           }
6317         if (memcmp(type,mng_BASI,4) == 0)
6318           {
6319             skip_to_iend=MagickTrue;
6320
6321             if (mng_info->basi_warning == 0)
6322               (void) ThrowMagickException(exception,GetMagickModule(),
6323                 CoderError,"BASI is not implemented yet","`%s'",
6324                 image->filename);
6325
6326             mng_info->basi_warning++;
6327 #ifdef MNG_BASI_SUPPORTED
6328             basi_width=(unsigned long) mng_get_long(p);
6329             basi_width=(unsigned long) mng_get_long(&p[4]);
6330             basi_color_type=p[8];
6331             basi_compression_method=p[9];
6332             basi_filter_type=p[10];
6333             basi_interlace_method=p[11];
6334             if (length > 11)
6335               basi_red=((png_uint_32) p[12] << 8) & (png_uint_32) p[13];
6336
6337             else
6338               basi_red=0;
6339
6340             if (length > 13)
6341               basi_green=((png_uint_32) p[14] << 8) & (png_uint_32) p[15];
6342
6343             else
6344               basi_green=0;
6345
6346             if (length > 15)
6347               basi_blue=((png_uint_32) p[16] << 8) & (png_uint_32) p[17];
6348
6349             else
6350               basi_blue=0;
6351
6352             if (length > 17)
6353               basi_alpha=((png_uint_32) p[18] << 8) & (png_uint_32) p[19];
6354
6355             else
6356               {
6357                 if (basi_sample_depth == 16)
6358                   basi_alpha=65535L;
6359                 else
6360                   basi_alpha=255;
6361               }
6362
6363             if (length > 19)
6364               basi_viewable=p[20];
6365
6366             else
6367               basi_viewable=0;
6368
6369 #endif
6370             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6371             continue;
6372           }
6373
6374         if (memcmp(type,mng_IHDR,4)
6375 #if defined(JNG_SUPPORTED)
6376             && memcmp(type,mng_JHDR,4)
6377 #endif
6378             )
6379           {
6380             /* Not an IHDR or JHDR chunk */
6381             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6382
6383             continue;
6384           }
6385 /* Process IHDR */
6386         if (logging != MagickFalse)
6387           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6388             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6389
6390         mng_info->exists[object_id]=MagickTrue;
6391         mng_info->viewable[object_id]=MagickTrue;
6392
6393         if (mng_info->invisible[object_id])
6394           {
6395             if (logging != MagickFalse)
6396               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6397                 "  Skipping invisible object");
6398
6399             skip_to_iend=MagickTrue;
6400             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6401             continue;
6402           }
6403 #if defined(MNG_INSERT_LAYERS)
6404         if (length < 8)
6405           {
6406             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6407             ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6408           }
6409
6410         image_width=(size_t) mng_get_long(p);
6411         image_height=(size_t) mng_get_long(&p[4]);
6412 #endif
6413         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6414
6415         /*
6416           Insert a transparent background layer behind the entire animation
6417           if it is not full screen.
6418         */
6419 #if defined(MNG_INSERT_LAYERS)
6420         if (insert_layers && mng_type && first_mng_object)
6421           {
6422             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6423                 (image_width < mng_info->mng_width) ||
6424                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6425                 (image_height < mng_info->mng_height) ||
6426                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6427               {
6428                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6429                   {
6430                     /*
6431                       Allocate next image structure.
6432                     */
6433                     AcquireNextImage(image_info,image,exception);
6434
6435                     if (GetNextImageInList(image) == (Image *) NULL)
6436                       return(DestroyImageList(image));
6437
6438                     image=SyncNextImageInList(image);
6439                   }
6440                 mng_info->image=image;
6441
6442                 if (term_chunk_found)
6443                   {
6444                     image->start_loop=MagickTrue;
6445                     image->iterations=mng_iterations;
6446                     term_chunk_found=MagickFalse;
6447                   }
6448
6449                 else
6450                     image->start_loop=MagickFalse;
6451
6452                 /* Make a background rectangle.  */
6453
6454                 image->delay=0;
6455                 image->columns=mng_info->mng_width;
6456                 image->rows=mng_info->mng_height;
6457                 image->page.width=mng_info->mng_width;
6458                 image->page.height=mng_info->mng_height;
6459                 image->page.x=0;
6460                 image->page.y=0;
6461                 image->background_color=mng_background_color;
6462                 (void) SetImageBackgroundColor(image,exception);
6463                 if (logging != MagickFalse)
6464                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6465                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6466                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6467               }
6468           }
6469         /*
6470           Insert a background layer behind the upcoming image if
6471           framing_mode is 3, and we haven't already inserted one.
6472         */
6473         if (insert_layers && (mng_info->framing_mode == 3) &&
6474                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6475                 (simplicity & 0x08)))
6476           {
6477             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6478             {
6479               /*
6480                 Allocate next image structure.
6481               */
6482               AcquireNextImage(image_info,image,exception);
6483
6484               if (GetNextImageInList(image) == (Image *) NULL)
6485                 return(DestroyImageList(image));
6486
6487               image=SyncNextImageInList(image);
6488             }
6489
6490             mng_info->image=image;
6491
6492             if (term_chunk_found)
6493               {
6494                 image->start_loop=MagickTrue;
6495                 image->iterations=mng_iterations;
6496                 term_chunk_found=MagickFalse;
6497               }
6498
6499             else
6500                 image->start_loop=MagickFalse;
6501
6502             image->delay=0;
6503             image->columns=subframe_width;
6504             image->rows=subframe_height;
6505             image->page.width=subframe_width;
6506             image->page.height=subframe_height;
6507             image->page.x=mng_info->clip.left;
6508             image->page.y=mng_info->clip.top;
6509             image->background_color=mng_background_color;
6510             image->alpha_trait=UndefinedPixelTrait;
6511             (void) SetImageBackgroundColor(image,exception);
6512
6513             if (logging != MagickFalse)
6514               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6515                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6516                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6517                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6518           }
6519 #endif /* MNG_INSERT_LAYERS */
6520         first_mng_object=MagickFalse;
6521
6522         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6523           {
6524             /*
6525               Allocate next image structure.
6526             */
6527             AcquireNextImage(image_info,image,exception);
6528
6529             if (GetNextImageInList(image) == (Image *) NULL)
6530               return(DestroyImageList(image));
6531
6532             image=SyncNextImageInList(image);
6533           }
6534         mng_info->image=image;
6535         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6536           GetBlobSize(image));
6537
6538         if (status == MagickFalse)
6539           break;
6540
6541         if (term_chunk_found)
6542           {
6543             image->start_loop=MagickTrue;
6544             term_chunk_found=MagickFalse;
6545           }
6546
6547         else
6548             image->start_loop=MagickFalse;
6549
6550         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6551           {
6552             image->delay=frame_delay;
6553             frame_delay=default_frame_delay;
6554           }
6555
6556         else
6557           image->delay=0;
6558
6559         image->page.width=mng_info->mng_width;
6560         image->page.height=mng_info->mng_height;
6561         image->page.x=mng_info->x_off[object_id];
6562         image->page.y=mng_info->y_off[object_id];
6563         image->iterations=mng_iterations;
6564
6565         /*
6566           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6567         */
6568
6569         if (logging != MagickFalse)
6570           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6571             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6572             type[2],type[3]);
6573
6574         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6575
6576         if (offset < 0)
6577           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6578       }
6579
6580     mng_info->image=image;
6581     mng_info->mng_type=mng_type;
6582     mng_info->object_id=object_id;
6583
6584     if (memcmp(type,mng_IHDR,4) == 0)
6585       image=ReadOnePNGImage(mng_info,image_info,exception);
6586
6587 #if defined(JNG_SUPPORTED)
6588     else
6589       image=ReadOneJNGImage(mng_info,image_info,exception);
6590 #endif
6591
6592     if (image == (Image *) NULL)
6593       {
6594         if (logging != MagickFalse)
6595           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6596             "exit ReadJNGImage() with error");
6597
6598         return((Image *) NULL);
6599       }
6600
6601     if (image->columns == 0 || image->rows == 0)
6602       {
6603         (void) CloseBlob(image);
6604         return(DestroyImageList(image));
6605       }
6606
6607     mng_info->image=image;
6608
6609     if (mng_type)
6610       {
6611         MngBox
6612           crop_box;
6613
6614         if (mng_info->magn_methx || mng_info->magn_methy)
6615           {
6616             png_uint_32
6617                magnified_height,
6618                magnified_width;
6619
6620             if (logging != MagickFalse)
6621               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6622                 "  Processing MNG MAGN chunk");
6623
6624             if (mng_info->magn_methx == 1)
6625               {
6626                 magnified_width=mng_info->magn_ml;
6627
6628                 if (image->columns > 1)
6629                    magnified_width += mng_info->magn_mr;
6630
6631                 if (image->columns > 2)
6632                    magnified_width += (png_uint_32)
6633                       ((image->columns-2)*(mng_info->magn_mx));
6634               }
6635
6636             else
6637               {
6638                 magnified_width=(png_uint_32) image->columns;
6639
6640                 if (image->columns > 1)
6641                    magnified_width += mng_info->magn_ml-1;
6642
6643                 if (image->columns > 2)
6644                    magnified_width += mng_info->magn_mr-1;
6645
6646                 if (image->columns > 3)
6647                    magnified_width += (png_uint_32)
6648                       ((image->columns-3)*(mng_info->magn_mx-1));
6649               }
6650
6651             if (mng_info->magn_methy == 1)
6652               {
6653                 magnified_height=mng_info->magn_mt;
6654
6655                 if (image->rows > 1)
6656                    magnified_height += mng_info->magn_mb;
6657
6658                 if (image->rows > 2)
6659                    magnified_height += (png_uint_32)
6660                       ((image->rows-2)*(mng_info->magn_my));
6661               }
6662
6663             else
6664               {
6665                 magnified_height=(png_uint_32) image->rows;
6666
6667                 if (image->rows > 1)
6668                    magnified_height += mng_info->magn_mt-1;
6669
6670                 if (image->rows > 2)
6671                    magnified_height += mng_info->magn_mb-1;
6672
6673                 if (image->rows > 3)
6674                    magnified_height += (png_uint_32)
6675                       ((image->rows-3)*(mng_info->magn_my-1));
6676               }
6677
6678             if (magnified_height > image->rows ||
6679                 magnified_width > image->columns)
6680               {
6681                 Image
6682                   *large_image;
6683
6684                 int
6685                   yy;
6686
6687                 Quantum
6688                   *next,
6689                   *prev;
6690
6691                 png_uint_16
6692                   magn_methx,
6693                   magn_methy;
6694
6695                 ssize_t
6696                   m,
6697                   y;
6698
6699                 register Quantum
6700                   *n,
6701                   *q;
6702
6703                 register ssize_t
6704                   x;
6705
6706                 /* Allocate next image structure.  */
6707
6708                 if (logging != MagickFalse)
6709                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6710                     "    Allocate magnified image");
6711
6712                 AcquireNextImage(image_info,image,exception);
6713
6714                 if (GetNextImageInList(image) == (Image *) NULL)
6715                   return(DestroyImageList(image));
6716
6717                 large_image=SyncNextImageInList(image);
6718
6719                 large_image->columns=magnified_width;
6720                 large_image->rows=magnified_height;
6721
6722                 magn_methx=mng_info->magn_methx;
6723                 magn_methy=mng_info->magn_methy;
6724
6725 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6726 #define QM unsigned short
6727                 if (magn_methx != 1 || magn_methy != 1)
6728                   {
6729                   /*
6730                      Scale pixels to unsigned shorts to prevent
6731                      overflow of intermediate values of interpolations
6732                   */
6733                      for (y=0; y < (ssize_t) image->rows; y++)
6734                      {
6735                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6736                           exception);
6737
6738                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6739                        {
6740                           SetPixelRed(image,ScaleQuantumToShort(
6741                             GetPixelRed(image,q)),q);
6742                           SetPixelGreen(image,ScaleQuantumToShort(
6743                             GetPixelGreen(image,q)),q);
6744                           SetPixelBlue(image,ScaleQuantumToShort(
6745                             GetPixelBlue(image,q)),q);
6746                           SetPixelAlpha(image,ScaleQuantumToShort(
6747                             GetPixelAlpha(image,q)),q);
6748                           q+=GetPixelChannels(image);
6749                        }
6750
6751                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6752                          break;
6753                      }
6754                   }
6755 #else
6756 #define QM Quantum
6757 #endif
6758
6759                 if (image->alpha_trait != UndefinedPixelTrait)
6760                    (void) SetImageBackgroundColor(large_image,exception);
6761
6762                 else
6763                   {
6764                     large_image->background_color.alpha=OpaqueAlpha;
6765                     (void) SetImageBackgroundColor(large_image,exception);
6766
6767                     if (magn_methx == 4)
6768                       magn_methx=2;
6769
6770                     if (magn_methx == 5)
6771                       magn_methx=3;
6772
6773                     if (magn_methy == 4)
6774                       magn_methy=2;
6775
6776                     if (magn_methy == 5)
6777                       magn_methy=3;
6778                   }
6779
6780                 /* magnify the rows into the right side of the large image */
6781
6782                 if (logging != MagickFalse)
6783                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6784                     "    Magnify the rows to %.20g",
6785                     (double) large_image->rows);
6786                 m=(ssize_t) mng_info->magn_mt;
6787                 yy=0;
6788                 length=(size_t) GetPixelChannels(image)*image->columns;
6789                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6790                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6791
6792                 if ((prev == (Quantum *) NULL) ||
6793                     (next == (Quantum *) NULL))
6794                   {
6795                      image=DestroyImageList(image);
6796                      ThrowReaderException(ResourceLimitError,
6797                        "MemoryAllocationFailed");
6798                   }
6799
6800                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6801                 (void) CopyMagickMemory(next,n,length);
6802
6803                 for (y=0; y < (ssize_t) image->rows; y++)
6804                 {
6805                   if (y == 0)
6806                     m=(ssize_t) mng_info->magn_mt;
6807
6808                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6809                     m=(ssize_t) mng_info->magn_mb;
6810
6811                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6812                     m=(ssize_t) mng_info->magn_mb;
6813
6814                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6815                     m=1;
6816
6817                   else
6818                     m=(ssize_t) mng_info->magn_my;
6819
6820                   n=prev;
6821                   prev=next;
6822                   next=n;
6823
6824                   if (y < (ssize_t) image->rows-1)
6825                     {
6826                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6827                           exception);
6828                       (void) CopyMagickMemory(next,n,length);
6829                     }
6830
6831                   for (i=0; i < m; i++, yy++)
6832                   {
6833                     register Quantum
6834                       *pixels;
6835
6836                     assert(yy < (ssize_t) large_image->rows);
6837                     pixels=prev;
6838                     n=next;
6839                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6840                       1,exception);
6841                     q+=(large_image->columns-image->columns)*
6842                       GetPixelChannels(large_image);
6843
6844                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6845                     {
6846                       /* To do: get color as function of indexes[x] */
6847                       /*
6848                       if (image->storage_class == PseudoClass)
6849                         {
6850                         }
6851                       */
6852
6853                       if (magn_methy <= 1)
6854                         {
6855                           /* replicate previous */
6856                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6857                           SetPixelGreen(large_image,GetPixelGreen(image,
6858                              pixels),q);
6859                           SetPixelBlue(large_image,GetPixelBlue(image,
6860                              pixels),q);
6861                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6862                              pixels),q);
6863                         }
6864
6865                       else if (magn_methy == 2 || magn_methy == 4)
6866                         {
6867                           if (i == 0)
6868                             {
6869                               SetPixelRed(large_image,GetPixelRed(image,
6870                                  pixels),q);
6871                               SetPixelGreen(large_image,GetPixelGreen(image,
6872                                  pixels),q);
6873                               SetPixelBlue(large_image,GetPixelBlue(image,
6874                                  pixels),q);
6875                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6876                                  pixels),q);
6877                             }
6878
6879                           else
6880                             {
6881                               /* Interpolate */
6882                               SetPixelRed(large_image,((QM) (((ssize_t)
6883                                  (2*i*(GetPixelRed(image,n)
6884                                  -GetPixelRed(image,pixels)+m))/
6885                                  ((ssize_t) (m*2))
6886                                  +GetPixelRed(image,pixels)))),q);
6887                               SetPixelGreen(large_image,((QM) (((ssize_t)
6888                                  (2*i*(GetPixelGreen(image,n)
6889                                  -GetPixelGreen(image,pixels)+m))/
6890                                  ((ssize_t) (m*2))
6891                                  +GetPixelGreen(image,pixels)))),q);
6892                               SetPixelBlue(large_image,((QM) (((ssize_t)
6893                                  (2*i*(GetPixelBlue(image,n)
6894                                  -GetPixelBlue(image,pixels)+m))/
6895                                  ((ssize_t) (m*2))
6896                                  +GetPixelBlue(image,pixels)))),q);
6897
6898                               if (image->alpha_trait != UndefinedPixelTrait)
6899                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6900                                     (2*i*(GetPixelAlpha(image,n)
6901                                     -GetPixelAlpha(image,pixels)+m))
6902                                     /((ssize_t) (m*2))+
6903                                    GetPixelAlpha(image,pixels)))),q);
6904                             }
6905
6906                           if (magn_methy == 4)
6907                             {
6908                               /* Replicate nearest */
6909                               if (i <= ((m+1) << 1))
6910                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6911                                     pixels),q);
6912                               else
6913                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6914                                     n),q);
6915                             }
6916                         }
6917
6918                       else /* if (magn_methy == 3 || magn_methy == 5) */
6919                         {
6920                           /* Replicate nearest */
6921                           if (i <= ((m+1) << 1))
6922                           {
6923                              SetPixelRed(large_image,GetPixelRed(image,
6924                                     pixels),q);
6925                              SetPixelGreen(large_image,GetPixelGreen(image,
6926                                     pixels),q);
6927                              SetPixelBlue(large_image,GetPixelBlue(image,
6928                                     pixels),q);
6929                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6930                                     pixels),q);
6931                           }
6932
6933                           else
6934                           {
6935                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6936                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6937                                     q);
6938                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6939                                     q);
6940                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6941                                     q);
6942                           }
6943
6944                           if (magn_methy == 5)
6945                             {
6946                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6947                                  (GetPixelAlpha(image,n)
6948                                  -GetPixelAlpha(image,pixels))
6949                                  +m))/((ssize_t) (m*2))
6950                                  +GetPixelAlpha(image,pixels)),q);
6951                             }
6952                         }
6953                       n+=GetPixelChannels(image);
6954                       q+=GetPixelChannels(large_image);
6955                       pixels+=GetPixelChannels(image);
6956                     } /* x */
6957
6958                     if (SyncAuthenticPixels(large_image,exception) == 0)
6959                       break;
6960
6961                   } /* i */
6962                 } /* y */
6963
6964                 prev=(Quantum *) RelinquishMagickMemory(prev);
6965                 next=(Quantum *) RelinquishMagickMemory(next);
6966
6967                 length=image->columns;
6968
6969                 if (logging != MagickFalse)
6970                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6971                     "    Delete original image");
6972
6973                 DeleteImageFromList(&image);
6974
6975                 image=large_image;
6976
6977                 mng_info->image=image;
6978
6979                 /* magnify the columns */
6980                 if (logging != MagickFalse)
6981                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6982                     "    Magnify the columns to %.20g",
6983                     (double) image->columns);
6984
6985                 for (y=0; y < (ssize_t) image->rows; y++)
6986                 {
6987                   register Quantum
6988                     *pixels;
6989
6990                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6991                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6992                   n=pixels+GetPixelChannels(image);
6993
6994                   for (x=(ssize_t) (image->columns-length);
6995                     x < (ssize_t) image->columns; x++)
6996                   {
6997                     /* To do: Rewrite using Get/Set***PixelChannel() */
6998
6999                     if (x == (ssize_t) (image->columns-length))
7000                       m=(ssize_t) mng_info->magn_ml;
7001
7002                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
7003                       m=(ssize_t) mng_info->magn_mr;
7004
7005                     else if (magn_methx <= 1 &&
7006                         x == (ssize_t) image->columns-1)
7007                       m=(ssize_t) mng_info->magn_mr;
7008
7009                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
7010                       m=1;
7011
7012                     else
7013                       m=(ssize_t) mng_info->magn_mx;
7014
7015                     for (i=0; i < m; i++)
7016                     {
7017                       if (magn_methx <= 1)
7018                         {
7019                           /* replicate previous */
7020                           SetPixelRed(image,GetPixelRed(image,pixels),q);
7021                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7022                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7023                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7024                         }
7025
7026                       else if (magn_methx == 2 || magn_methx == 4)
7027                         {
7028                           if (i == 0)
7029                           {
7030                             SetPixelRed(image,GetPixelRed(image,pixels),q);
7031                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
7032                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7033                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
7034                           }
7035
7036                           /* To do: Rewrite using Get/Set***PixelChannel() */
7037                           else
7038                             {
7039                               /* Interpolate */
7040                               SetPixelRed(image,(QM) ((2*i*(
7041                                  GetPixelRed(image,n)
7042                                  -GetPixelRed(image,pixels))+m)
7043                                  /((ssize_t) (m*2))+
7044                                  GetPixelRed(image,pixels)),q);
7045
7046                               SetPixelGreen(image,(QM) ((2*i*(
7047                                  GetPixelGreen(image,n)
7048                                  -GetPixelGreen(image,pixels))+m)
7049                                  /((ssize_t) (m*2))+
7050                                  GetPixelGreen(image,pixels)),q);
7051
7052                               SetPixelBlue(image,(QM) ((2*i*(
7053                                  GetPixelBlue(image,n)
7054                                  -GetPixelBlue(image,pixels))+m)
7055                                  /((ssize_t) (m*2))+
7056                                  GetPixelBlue(image,pixels)),q);
7057                               if (image->alpha_trait != UndefinedPixelTrait)
7058                                  SetPixelAlpha(image,(QM) ((2*i*(
7059                                    GetPixelAlpha(image,n)
7060                                    -GetPixelAlpha(image,pixels))+m)
7061                                    /((ssize_t) (m*2))+
7062                                    GetPixelAlpha(image,pixels)),q);
7063                             }
7064
7065                           if (magn_methx == 4)
7066                             {
7067                               /* Replicate nearest */
7068                               if (i <= ((m+1) << 1))
7069                               {
7070                                  SetPixelAlpha(image,
7071                                    GetPixelAlpha(image,pixels)+0,q);
7072                               }
7073                               else
7074                               {
7075                                  SetPixelAlpha(image,
7076                                    GetPixelAlpha(image,n)+0,q);
7077                               }
7078                             }
7079                         }
7080
7081                       else /* if (magn_methx == 3 || magn_methx == 5) */
7082                         {
7083                           /* Replicate nearest */
7084                           if (i <= ((m+1) << 1))
7085                           {
7086                              SetPixelRed(image,GetPixelRed(image,pixels),q);
7087                              SetPixelGreen(image,GetPixelGreen(image,
7088                                  pixels),q);
7089                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
7090                              SetPixelAlpha(image,GetPixelAlpha(image,
7091                                  pixels),q);
7092                           }
7093
7094                           else
7095                           {
7096                              SetPixelRed(image,GetPixelRed(image,n),q);
7097                              SetPixelGreen(image,GetPixelGreen(image,n),q);
7098                              SetPixelBlue(image,GetPixelBlue(image,n),q);
7099                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
7100                           }
7101
7102                           if (magn_methx == 5)
7103                             {
7104                               /* Interpolate */
7105                               SetPixelAlpha(image,
7106                                  (QM) ((2*i*( GetPixelAlpha(image,n)
7107                                  -GetPixelAlpha(image,pixels))+m)/
7108                                  ((ssize_t) (m*2))
7109                                  +GetPixelAlpha(image,pixels)),q);
7110                             }
7111                         }
7112                       q+=GetPixelChannels(image);
7113                     }
7114                     n+=GetPixelChannels(image);
7115                   }
7116
7117                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
7118                     break;
7119                 }
7120 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7121               if (magn_methx != 1 || magn_methy != 1)
7122                 {
7123                 /*
7124                    Rescale pixels to Quantum
7125                 */
7126                    for (y=0; y < (ssize_t) image->rows; y++)
7127                    {
7128                      q=GetAuthenticPixels(image,0,y,image->columns,1,
7129                        exception);
7130
7131                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
7132                      {
7133                         SetPixelRed(image,ScaleShortToQuantum(
7134                           GetPixelRed(image,q)),q);
7135                         SetPixelGreen(image,ScaleShortToQuantum(
7136                           GetPixelGreen(image,q)),q);
7137                         SetPixelBlue(image,ScaleShortToQuantum(
7138                           GetPixelBlue(image,q)),q);
7139                         SetPixelAlpha(image,ScaleShortToQuantum(
7140                           GetPixelAlpha(image,q)),q);
7141                         q+=GetPixelChannels(image);
7142                      }
7143
7144                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
7145                        break;
7146                    }
7147                 }
7148 #endif
7149                 if (logging != MagickFalse)
7150                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7151                     "  Finished MAGN processing");
7152               }
7153           }
7154
7155         /*
7156           Crop_box is with respect to the upper left corner of the MNG.
7157         */
7158         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
7159         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
7160         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
7161         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
7162         crop_box=mng_minimum_box(crop_box,mng_info->clip);
7163         crop_box=mng_minimum_box(crop_box,mng_info->frame);
7164         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
7165         if ((crop_box.left != (mng_info->image_box.left
7166             +mng_info->x_off[object_id])) ||
7167             (crop_box.right != (mng_info->image_box.right
7168             +mng_info->x_off[object_id])) ||
7169             (crop_box.top != (mng_info->image_box.top
7170             +mng_info->y_off[object_id])) ||
7171             (crop_box.bottom != (mng_info->image_box.bottom
7172             +mng_info->y_off[object_id])))
7173           {
7174             if (logging != MagickFalse)
7175               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7176                 "  Crop the PNG image");
7177
7178             if ((crop_box.left < crop_box.right) &&
7179                 (crop_box.top < crop_box.bottom))
7180               {
7181                 Image
7182                   *im;
7183
7184                 RectangleInfo
7185                   crop_info;
7186
7187                 /*
7188                   Crop_info is with respect to the upper left corner of
7189                   the image.
7190                 */
7191                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7192                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7193                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7194                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7195                 image->page.width=image->columns;
7196                 image->page.height=image->rows;
7197                 image->page.x=0;
7198                 image->page.y=0;
7199                 im=CropImage(image,&crop_info,exception);
7200
7201                 if (im != (Image *) NULL)
7202                   {
7203                     image->columns=im->columns;
7204                     image->rows=im->rows;
7205                     im=DestroyImage(im);
7206                     image->page.width=image->columns;
7207                     image->page.height=image->rows;
7208                     image->page.x=crop_box.left;
7209                     image->page.y=crop_box.top;
7210                   }
7211               }
7212
7213             else
7214               {
7215                 /*
7216                   No pixels in crop area.  The MNG spec still requires
7217                   a layer, though, so make a single transparent pixel in
7218                   the top left corner.
7219                 */
7220                 image->columns=1;
7221                 image->rows=1;
7222                 image->colors=2;
7223                 (void) SetImageBackgroundColor(image,exception);
7224                 image->page.width=1;
7225                 image->page.height=1;
7226                 image->page.x=0;
7227                 image->page.y=0;
7228               }
7229           }
7230 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7231         image=mng_info->image;
7232 #endif
7233       }
7234
7235 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7236       /* PNG does not handle depths greater than 16 so reduce it even
7237        * if lossy.
7238        */
7239       if (image->depth > 16)
7240          image->depth=16;
7241 #endif
7242
7243 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7244       if (image->depth > 8)
7245         {
7246           /* To do: fill low byte properly */
7247           image->depth=16;
7248         }
7249
7250       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7251          image->depth = 8;
7252 #endif
7253
7254       if (image_info->number_scenes != 0)
7255         {
7256           if (mng_info->scenes_found >
7257              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7258             break;
7259         }
7260
7261       if (logging != MagickFalse)
7262         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7263           "  Finished reading image datastream.");
7264
7265   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7266
7267   (void) CloseBlob(image);
7268
7269   if (logging != MagickFalse)
7270     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7271       "  Finished reading all image datastreams.");
7272
7273 #if defined(MNG_INSERT_LAYERS)
7274   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7275        (mng_info->mng_height))
7276     {
7277       /*
7278         Insert a background layer if nothing else was found.
7279       */
7280       if (logging != MagickFalse)
7281         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7282           "  No images found.  Inserting a background layer.");
7283
7284       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7285         {
7286           /*
7287             Allocate next image structure.
7288           */
7289           AcquireNextImage(image_info,image,exception);
7290           if (GetNextImageInList(image) == (Image *) NULL)
7291             {
7292               if (logging != MagickFalse)
7293                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7294                   "  Allocation failed, returning NULL.");
7295
7296               return(DestroyImageList(image));;
7297             }
7298           image=SyncNextImageInList(image);
7299         }
7300       image->columns=mng_info->mng_width;
7301       image->rows=mng_info->mng_height;
7302       image->page.width=mng_info->mng_width;
7303       image->page.height=mng_info->mng_height;
7304       image->page.x=0;
7305       image->page.y=0;
7306       image->background_color=mng_background_color;
7307       image->alpha_trait=UndefinedPixelTrait;
7308
7309       if (image_info->ping == MagickFalse)
7310         (void) SetImageBackgroundColor(image,exception);
7311
7312       mng_info->image_found++;
7313     }
7314 #endif
7315   image->iterations=mng_iterations;
7316
7317   if (mng_iterations == 1)
7318     image->start_loop=MagickTrue;
7319
7320   while (GetPreviousImageInList(image) != (Image *) NULL)
7321   {
7322     image_count++;
7323     if (image_count > 10*mng_info->image_found)
7324       {
7325         if (logging != MagickFalse)
7326           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7327
7328         (void) ThrowMagickException(exception,GetMagickModule(),
7329           CoderError,"Linked list is corrupted, beginning of list not found",
7330           "`%s'",image_info->filename);
7331
7332         return(DestroyImageList(image));
7333       }
7334
7335     image=GetPreviousImageInList(image);
7336
7337     if (GetNextImageInList(image) == (Image *) NULL)
7338       {
7339         if (logging != MagickFalse)
7340           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7341
7342         (void) ThrowMagickException(exception,GetMagickModule(),
7343           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7344           image_info->filename);
7345       }
7346   }
7347
7348   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7349              GetNextImageInList(image) ==
7350      (Image *) NULL)
7351     {
7352       if (logging != MagickFalse)
7353         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7354             "  First image null");
7355
7356       (void) ThrowMagickException(exception,GetMagickModule(),
7357         CoderError,"image->next for first image is NULL but shouldn't be.",
7358         "`%s'",image_info->filename);
7359     }
7360
7361   if (mng_info->image_found == 0)
7362     {
7363       if (logging != MagickFalse)
7364         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7365           "  No visible images found.");
7366
7367       (void) ThrowMagickException(exception,GetMagickModule(),
7368         CoderError,"No visible images in file","`%s'",image_info->filename);
7369
7370       return(DestroyImageList(image));
7371     }
7372
7373   if (mng_info->ticks_per_second)
7374     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7375             final_delay/mng_info->ticks_per_second;
7376
7377   else
7378     image->start_loop=MagickTrue;
7379
7380   /* Find final nonzero image delay */
7381   final_image_delay=0;
7382
7383   while (GetNextImageInList(image) != (Image *) NULL)
7384     {
7385       if (image->delay)
7386         final_image_delay=image->delay;
7387
7388       image=GetNextImageInList(image);
7389     }
7390
7391   if (final_delay < final_image_delay)
7392     final_delay=final_image_delay;
7393
7394   image->delay=final_delay;
7395
7396   if (logging != MagickFalse)
7397       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7398         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7399         (double) final_delay);
7400
7401   if (logging != MagickFalse)
7402     {
7403       int
7404         scene;
7405
7406       scene=0;
7407       image=GetFirstImageInList(image);
7408
7409       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7410         "  Before coalesce:");
7411
7412       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7413         "    scene 0 delay=%.20g",(double) image->delay);
7414
7415       while (GetNextImageInList(image) != (Image *) NULL)
7416       {
7417         image=GetNextImageInList(image);
7418         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7419           "    scene %.20g delay=%.20g",(double) scene++,
7420           (double) image->delay);
7421       }
7422     }
7423
7424   image=GetFirstImageInList(image);
7425 #ifdef MNG_COALESCE_LAYERS
7426   if (insert_layers)
7427     {
7428       Image
7429         *next_image,
7430         *next;
7431
7432       size_t
7433         scene;
7434
7435       if (logging != MagickFalse)
7436         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7437           "  Coalesce Images");
7438
7439       scene=image->scene;
7440       next_image=CoalesceImages(image,exception);
7441
7442       if (next_image == (Image *) NULL)
7443         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7444
7445       image=DestroyImageList(image);
7446       image=next_image;
7447
7448       for (next=image; next != (Image *) NULL; next=next_image)
7449       {
7450          next->page.width=mng_info->mng_width;
7451          next->page.height=mng_info->mng_height;
7452          next->page.x=0;
7453          next->page.y=0;
7454          next->scene=scene++;
7455          next_image=GetNextImageInList(next);
7456
7457          if (next_image == (Image *) NULL)
7458            break;
7459
7460          if (next->delay == 0)
7461            {
7462              scene--;
7463              next_image->previous=GetPreviousImageInList(next);
7464              if (GetPreviousImageInList(next) == (Image *) NULL)
7465                image=next_image;
7466              else
7467                next->previous->next=next_image;
7468              next=DestroyImage(next);
7469            }
7470       }
7471     }
7472 #endif
7473
7474   while (GetNextImageInList(image) != (Image *) NULL)
7475       image=GetNextImageInList(image);
7476
7477   image->dispose=BackgroundDispose;
7478
7479   if (logging != MagickFalse)
7480     {
7481       int
7482         scene;
7483
7484       scene=0;
7485       image=GetFirstImageInList(image);
7486
7487       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7488         "  After coalesce:");
7489
7490       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7491         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7492         (double) image->dispose);
7493
7494       while (GetNextImageInList(image) != (Image *) NULL)
7495       {
7496         image=GetNextImageInList(image);
7497
7498         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7499           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7500           (double) image->delay,(double) image->dispose);
7501       }
7502    }
7503
7504   if (logging != MagickFalse)
7505     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7506       "  exit ReadOneMNGImage();");
7507
7508   return(image);
7509 }
7510
7511 static Image *ReadMNGImage(const ImageInfo *image_info,
7512      ExceptionInfo *exception)
7513 {
7514   Image
7515     *image;
7516
7517   MagickBooleanType
7518     logging,
7519     status;
7520
7521   MngInfo
7522     *mng_info;
7523
7524   /* Open image file.  */
7525
7526   assert(image_info != (const ImageInfo *) NULL);
7527   assert(image_info->signature == MagickCoreSignature);
7528   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
7529      image_info->filename);
7530   assert(exception != (ExceptionInfo *) NULL);
7531   assert(exception->signature == MagickCoreSignature);
7532   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
7533   image=AcquireImage(image_info,exception);
7534   mng_info=(MngInfo *) NULL;
7535   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
7536
7537   if (status == MagickFalse)
7538     return((Image *) NULL);
7539
7540   /* Allocate a MngInfo structure.  */
7541
7542   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
7543
7544   if (mng_info == (MngInfo *) NULL)
7545     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7546
7547   /* Initialize members of the MngInfo structure.  */
7548
7549   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
7550   mng_info->image=image;
7551   image=ReadOneMNGImage(mng_info,image_info,exception);
7552   mng_info=MngInfoFreeStruct(mng_info);
7553
7554   if (image == (Image *) NULL)
7555     {
7556       if (logging != MagickFalse)
7557         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7558           "exit ReadMNGImage() with error");
7559
7560       return((Image *) NULL);
7561     }
7562   (void) CloseBlob(image);
7563
7564   if (logging != MagickFalse)
7565     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7566
7567   return(GetFirstImageInList(image));
7568 }
7569 #else /* PNG_LIBPNG_VER > 10011 */
7570 static Image *ReadPNGImage(const ImageInfo *image_info,
7571    ExceptionInfo *exception)
7572 {
7573   printf("Your PNG library is too old: You have libpng-%s\n",
7574      PNG_LIBPNG_VER_STRING);
7575
7576   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7577     "PNG library is too old","`%s'",image_info->filename);
7578
7579   return(Image *) NULL;
7580 }
7581
7582 static Image *ReadMNGImage(const ImageInfo *image_info,
7583    ExceptionInfo *exception)
7584 {
7585   return(ReadPNGImage(image_info,exception));
7586 }
7587 #endif /* PNG_LIBPNG_VER > 10011 */
7588 #endif
7589 \f
7590 /*
7591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7592 %                                                                             %
7593 %                                                                             %
7594 %                                                                             %
7595 %   R e g i s t e r P N G I m a g e                                           %
7596 %                                                                             %
7597 %                                                                             %
7598 %                                                                             %
7599 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7600 %
7601 %  RegisterPNGImage() adds properties for the PNG image format to
7602 %  the list of supported formats.  The properties include the image format
7603 %  tag, a method to read and/or write the format, whether the format
7604 %  supports the saving of more than one frame to the same file or blob,
7605 %  whether the format supports native in-memory I/O, and a brief
7606 %  description of the format.
7607 %
7608 %  The format of the RegisterPNGImage method is:
7609 %
7610 %      size_t RegisterPNGImage(void)
7611 %
7612 */
7613 ModuleExport size_t RegisterPNGImage(void)
7614 {
7615   char
7616     version[MagickPathExtent];
7617
7618   MagickInfo
7619     *entry;
7620
7621   static const char
7622     *PNGNote=
7623     {
7624       "See http://www.libpng.org/ for details about the PNG format."
7625     },
7626
7627     *JNGNote=
7628     {
7629       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7630       "format."
7631     },
7632
7633     *MNGNote=
7634     {
7635       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7636       "format."
7637     };
7638
7639   *version='\0';
7640
7641 #if defined(PNG_LIBPNG_VER_STRING)
7642   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7643   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,
7644    MagickPathExtent);
7645
7646   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7647     {
7648       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7649       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7650             MagickPathExtent);
7651     }
7652 #endif
7653
7654   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7655   entry->flags|=CoderDecoderSeekableStreamFlag;
7656
7657 #if defined(MAGICKCORE_PNG_DELEGATE)
7658   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7659   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7660 #endif
7661
7662   entry->magick=(IsImageFormatHandler *) IsMNG;
7663
7664   if (*version != '\0')
7665     entry->version=ConstantString(version);
7666
7667   entry->mime_type=ConstantString("video/x-mng");
7668   entry->note=ConstantString(MNGNote);
7669   (void) RegisterMagickInfo(entry);
7670
7671   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7672
7673 #if defined(MAGICKCORE_PNG_DELEGATE)
7674   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7675   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7676 #endif
7677
7678   entry->magick=(IsImageFormatHandler *) IsPNG;
7679   entry->flags|=CoderDecoderSeekableStreamFlag;
7680   entry->flags^=CoderAdjoinFlag;
7681   entry->mime_type=ConstantString("image/png");
7682
7683   if (*version != '\0')
7684     entry->version=ConstantString(version);
7685
7686   entry->note=ConstantString(PNGNote);
7687   (void) RegisterMagickInfo(entry);
7688
7689   entry=AcquireMagickInfo("PNG","PNG8",
7690     "8-bit indexed with optional binary transparency");
7691
7692 #if defined(MAGICKCORE_PNG_DELEGATE)
7693   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7694   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7695 #endif
7696
7697   entry->magick=(IsImageFormatHandler *) IsPNG;
7698   entry->flags|=CoderDecoderSeekableStreamFlag;
7699   entry->flags^=CoderAdjoinFlag;
7700   entry->mime_type=ConstantString("image/png");
7701   (void) RegisterMagickInfo(entry);
7702
7703   entry=AcquireMagickInfo("PNG","PNG24",
7704     "opaque or binary transparent 24-bit RGB");
7705   *version='\0';
7706
7707 #if defined(ZLIB_VERSION)
7708   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7709   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7710
7711   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7712     {
7713       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7714       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7715     }
7716 #endif
7717
7718   if (*version != '\0')
7719     entry->version=ConstantString(version);
7720
7721 #if defined(MAGICKCORE_PNG_DELEGATE)
7722   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7723   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7724 #endif
7725
7726   entry->magick=(IsImageFormatHandler *) IsPNG;
7727   entry->flags|=CoderDecoderSeekableStreamFlag;
7728   entry->flags^=CoderAdjoinFlag;
7729   entry->mime_type=ConstantString("image/png");
7730   (void) RegisterMagickInfo(entry);
7731
7732   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7733
7734 #if defined(MAGICKCORE_PNG_DELEGATE)
7735   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7736   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7737 #endif
7738
7739   entry->magick=(IsImageFormatHandler *) IsPNG;
7740   entry->flags|=CoderDecoderSeekableStreamFlag;
7741   entry->flags^=CoderAdjoinFlag;
7742   entry->mime_type=ConstantString("image/png");
7743   (void) RegisterMagickInfo(entry);
7744
7745   entry=AcquireMagickInfo("PNG","PNG48",
7746     "opaque or binary transparent 48-bit RGB");
7747
7748 #if defined(MAGICKCORE_PNG_DELEGATE)
7749   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7750   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7751 #endif
7752
7753   entry->magick=(IsImageFormatHandler *) IsPNG;
7754   entry->flags|=CoderDecoderSeekableStreamFlag;
7755   entry->flags^=CoderAdjoinFlag;
7756   entry->mime_type=ConstantString("image/png");
7757   (void) RegisterMagickInfo(entry);
7758
7759   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7760
7761 #if defined(MAGICKCORE_PNG_DELEGATE)
7762   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7763   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7764 #endif
7765
7766   entry->magick=(IsImageFormatHandler *) IsPNG;
7767   entry->flags|=CoderDecoderSeekableStreamFlag;
7768   entry->flags^=CoderAdjoinFlag;
7769   entry->mime_type=ConstantString("image/png");
7770   (void) RegisterMagickInfo(entry);
7771
7772   entry=AcquireMagickInfo("PNG","PNG00",
7773     "PNG inheriting bit-depth, color-type from original, if possible");
7774
7775 #if defined(MAGICKCORE_PNG_DELEGATE)
7776   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7777   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7778 #endif
7779
7780   entry->magick=(IsImageFormatHandler *) IsPNG;
7781   entry->flags|=CoderDecoderSeekableStreamFlag;
7782   entry->flags^=CoderAdjoinFlag;
7783   entry->mime_type=ConstantString("image/png");
7784   (void) RegisterMagickInfo(entry);
7785
7786   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7787
7788 #if defined(JNG_SUPPORTED)
7789 #if defined(MAGICKCORE_PNG_DELEGATE)
7790   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7791   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7792 #endif
7793 #endif
7794
7795   entry->magick=(IsImageFormatHandler *) IsJNG;
7796   entry->flags|=CoderDecoderSeekableStreamFlag;
7797   entry->flags^=CoderAdjoinFlag;
7798   entry->mime_type=ConstantString("image/x-jng");
7799   entry->note=ConstantString(JNGNote);
7800   (void) RegisterMagickInfo(entry);
7801
7802 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7803   ping_semaphore=AcquireSemaphoreInfo();
7804 #endif
7805
7806   return(MagickImageCoderSignature);
7807 }
7808 \f
7809 /*
7810 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7811 %                                                                             %
7812 %                                                                             %
7813 %                                                                             %
7814 %   U n r e g i s t e r P N G I m a g e                                       %
7815 %                                                                             %
7816 %                                                                             %
7817 %                                                                             %
7818 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7819 %
7820 %  UnregisterPNGImage() removes format registrations made by the
7821 %  PNG module from the list of supported formats.
7822 %
7823 %  The format of the UnregisterPNGImage method is:
7824 %
7825 %      UnregisterPNGImage(void)
7826 %
7827 */
7828 ModuleExport void UnregisterPNGImage(void)
7829 {
7830   (void) UnregisterMagickInfo("MNG");
7831   (void) UnregisterMagickInfo("PNG");
7832   (void) UnregisterMagickInfo("PNG8");
7833   (void) UnregisterMagickInfo("PNG24");
7834   (void) UnregisterMagickInfo("PNG32");
7835   (void) UnregisterMagickInfo("PNG48");
7836   (void) UnregisterMagickInfo("PNG64");
7837   (void) UnregisterMagickInfo("PNG00");
7838   (void) UnregisterMagickInfo("JNG");
7839
7840 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7841   if (ping_semaphore != (SemaphoreInfo *) NULL)
7842     RelinquishSemaphoreInfo(&ping_semaphore);
7843 #endif
7844 }
7845 \f
7846 #if defined(MAGICKCORE_PNG_DELEGATE)
7847 #if PNG_LIBPNG_VER > 10011
7848 /*
7849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7850 %                                                                             %
7851 %                                                                             %
7852 %                                                                             %
7853 %   W r i t e M N G I m a g e                                                 %
7854 %                                                                             %
7855 %                                                                             %
7856 %                                                                             %
7857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7858 %
7859 %  WriteMNGImage() writes an image in the Portable Network Graphics
7860 %  Group's "Multiple-image Network Graphics" encoded image format.
7861 %
7862 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7863 %
7864 %  The format of the WriteMNGImage method is:
7865 %
7866 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7867 %        Image *image,ExceptionInfo *exception)
7868 %
7869 %  A description of each parameter follows.
7870 %
7871 %    o image_info: the image info.
7872 %
7873 %    o image:  The image.
7874 %
7875 %    o exception: return any errors or warnings in this structure.
7876 %
7877 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7878 %    "To do" under ReadPNGImage):
7879 %
7880 %    Preserve all unknown and not-yet-handled known chunks found in input
7881 %    PNG file and copy them  into output PNG files according to the PNG
7882 %    copying rules.
7883 %
7884 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7885 %
7886 %    Improve selection of color type (use indexed-colour or indexed-colour
7887 %    with tRNS when 256 or fewer unique RGBA values are present).
7888 %
7889 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7890 %    This will be complicated if we limit ourselves to generating MNG-LC
7891 %    files.  For now we ignore disposal method 3 and simply overlay the next
7892 %    image on it.
7893 %
7894 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7895 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7896 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7897 %
7898 %    Check for identical sRGB and replace with a global sRGB (and remove
7899 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7900 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7901 %    local gAMA/cHRM with local sRGB if appropriate).
7902 %
7903 %    Check for identical sBIT chunks and write global ones.
7904 %
7905 %    Provide option to skip writing the signature tEXt chunks.
7906 %
7907 %    Use signatures to detect identical objects and reuse the first
7908 %    instance of such objects instead of writing duplicate objects.
7909 %
7910 %    Use a smaller-than-32k value of compression window size when
7911 %    appropriate.
7912 %
7913 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7914 %    ancillary text chunks and save profiles.
7915 %
7916 %    Provide an option to force LC files (to ensure exact framing rate)
7917 %    instead of VLC.
7918 %
7919 %    Provide an option to force VLC files instead of LC, even when offsets
7920 %    are present.  This will involve expanding the embedded images with a
7921 %    transparent region at the top and/or left.
7922 */
7923
7924 static void
7925 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7926    png_info *ping_info, unsigned char *profile_type, unsigned char
7927    *profile_description, unsigned char *profile_data, png_uint_32 length)
7928 {
7929    png_textp
7930      text;
7931
7932    register ssize_t
7933      i;
7934
7935    unsigned char
7936      *sp;
7937
7938    png_charp
7939      dp;
7940
7941    png_uint_32
7942      allocated_length,
7943      description_length;
7944
7945    unsigned char
7946      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7947
7948    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7949       return;
7950
7951    if (image_info->verbose)
7952      {
7953        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7954          (char *) profile_type, (double) length);
7955      }
7956
7957 #if PNG_LIBPNG_VER >= 10400
7958    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7959 #else
7960    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7961 #endif
7962    description_length=(png_uint_32) strlen((const char *) profile_description);
7963    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7964       + description_length);
7965 #if PNG_LIBPNG_VER >= 10400
7966    text[0].text=(png_charp) png_malloc(ping,
7967       (png_alloc_size_t) allocated_length);
7968    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7969 #else
7970    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7971    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7972 #endif
7973    text[0].key[0]='\0';
7974    (void) ConcatenateMagickString(text[0].key,
7975       "Raw profile type ",MagickPathExtent);
7976    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7977    sp=profile_data;
7978    dp=text[0].text;
7979    *dp++='\n';
7980    (void) CopyMagickString(dp,(const char *) profile_description,
7981      allocated_length);
7982    dp+=description_length;
7983    *dp++='\n';
7984    (void) FormatLocaleString(dp,allocated_length-
7985      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7986    dp+=8;
7987
7988    for (i=0; i < (ssize_t) length; i++)
7989    {
7990      if (i%36 == 0)
7991        *dp++='\n';
7992      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7993      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7994    }
7995
7996    *dp++='\n';
7997    *dp='\0';
7998    text[0].text_length=(png_size_t) (dp-text[0].text);
7999    text[0].compression=image_info->compression == NoCompression ||
8000      (image_info->compression == UndefinedCompression &&
8001      text[0].text_length < 128) ? -1 : 0;
8002
8003    if (text[0].text_length <= allocated_length)
8004      png_set_text(ping,ping_info,text,1);
8005
8006    png_free(ping,text[0].text);
8007    png_free(ping,text[0].key);
8008    png_free(ping,text);
8009 }
8010
8011 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
8012   const Quantum *p, const PixelInfo *q)
8013 {
8014   MagickRealType
8015     value;
8016
8017   value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
8018   if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
8019     return(MagickFalse);
8020   value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
8021   if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
8022     return(MagickFalse);
8023   value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
8024   if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
8025     return(MagickFalse);
8026
8027   return(MagickTrue);
8028 }
8029
8030 #if defined(PNG_tIME_SUPPORTED)
8031 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
8032   const char *date,ExceptionInfo *exception)
8033 {
8034   unsigned int
8035     day,
8036     hour,
8037     minute,
8038     month,
8039     second,
8040     year;
8041
8042   png_time
8043     ptime;
8044
8045   time_t
8046     ttime;
8047
8048   if (date != (const char *) NULL)
8049     {
8050       if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
8051           &second) != 6)
8052         {
8053           (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
8054             "Invalid date format specified for png:tIME","`%s'",
8055             image->filename);
8056           return;
8057         }
8058       ptime.year=(png_uint_16) year;
8059       ptime.month=(png_byte) month;
8060       ptime.day=(png_byte) day;
8061       ptime.hour=(png_byte) hour;
8062       ptime.minute=(png_byte) minute;
8063       ptime.second=(png_byte) second;
8064     }
8065   else
8066   {
8067     time(&ttime);
8068     png_convert_from_time_t(&ptime,ttime);
8069   }
8070   png_set_tIME(ping,info,&ptime);
8071 }
8072 #endif
8073
8074 /* Write one PNG image */
8075 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
8076   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
8077 {
8078   char
8079     im_vers[32],
8080     libpng_runv[32],
8081     libpng_vers[32],
8082     zlib_runv[32],
8083     zlib_vers[32];
8084
8085   Image
8086     *image;
8087
8088   ImageInfo
8089     *image_info;
8090
8091   char
8092     s[2];
8093
8094   const char
8095     *name,
8096     *property,
8097     *value;
8098
8099   const StringInfo
8100     *profile;
8101
8102   int
8103     num_passes,
8104     pass,
8105     ping_wrote_caNv;
8106
8107   png_byte
8108      ping_trans_alpha[256];
8109
8110   png_color
8111      palette[257];
8112
8113   png_color_16
8114     ping_background,
8115     ping_trans_color;
8116
8117   png_info
8118     *ping_info;
8119
8120   png_struct
8121     *ping;
8122
8123   png_uint_32
8124     ping_height,
8125     ping_width;
8126
8127   ssize_t
8128     y;
8129
8130   MagickBooleanType
8131     image_matte,
8132     logging,
8133     matte,
8134
8135     ping_have_blob,
8136     ping_have_cheap_transparency,
8137     ping_have_color,
8138     ping_have_non_bw,
8139     ping_have_PLTE,
8140     ping_have_bKGD,
8141     ping_have_eXIf,
8142     ping_have_iCCP,
8143     ping_have_pHYs,
8144     ping_have_sRGB,
8145     ping_have_tRNS,
8146
8147     ping_exclude_bKGD,
8148     ping_exclude_cHRM,
8149     ping_exclude_date,
8150     /* ping_exclude_EXIF, */
8151     ping_exclude_eXIf,
8152     ping_exclude_gAMA,
8153     ping_exclude_iCCP,
8154     /* ping_exclude_iTXt, */
8155     ping_exclude_oFFs,
8156     ping_exclude_pHYs,
8157     ping_exclude_sRGB,
8158     ping_exclude_tEXt,
8159     ping_exclude_tIME,
8160     /* ping_exclude_tRNS, */
8161     ping_exclude_vpAg,
8162     ping_exclude_caNv,
8163     ping_exclude_zCCP, /* hex-encoded iCCP */
8164     ping_exclude_zTXt,
8165
8166     ping_preserve_colormap,
8167     ping_preserve_iCCP,
8168     ping_need_colortype_warning,
8169
8170     status,
8171     tried_332,
8172     tried_333,
8173     tried_444;
8174
8175   MemoryInfo
8176     *volatile pixel_info;
8177
8178   QuantumInfo
8179     *quantum_info;
8180
8181   PNGErrorInfo
8182     error_info;
8183
8184   register ssize_t
8185     i,
8186     x;
8187
8188   unsigned char
8189     *ping_pixels;
8190
8191   volatile int
8192     image_colors,
8193     ping_bit_depth,
8194     ping_color_type,
8195     ping_interlace_method,
8196     ping_compression_method,
8197     ping_filter_method,
8198     ping_num_trans;
8199
8200   volatile size_t
8201     image_depth,
8202     old_bit_depth;
8203
8204   size_t
8205     quality,
8206     rowbytes,
8207     save_image_depth;
8208
8209   int
8210     j,
8211     number_colors,
8212     number_opaque,
8213     number_semitransparent,
8214     number_transparent,
8215     ping_pHYs_unit_type;
8216
8217   png_uint_32
8218     ping_pHYs_x_resolution,
8219     ping_pHYs_y_resolution;
8220
8221   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8222     "  Enter WriteOnePNGImage()");
8223
8224   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8225   if (image == (Image *) NULL)
8226     return(MagickFalse);
8227   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8228   if (image_info == (ImageInfo *) NULL)
8229     ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8230
8231   /* Define these outside of the following "if logging()" block so they will
8232    * show in debuggers.
8233    */
8234   *im_vers='\0';
8235   (void) ConcatenateMagickString(im_vers,
8236          MagickLibVersionText,MagickPathExtent);
8237   (void) ConcatenateMagickString(im_vers,
8238          MagickLibAddendum,MagickPathExtent);
8239
8240   *libpng_vers='\0';
8241   (void) ConcatenateMagickString(libpng_vers,
8242          PNG_LIBPNG_VER_STRING,32);
8243   *libpng_runv='\0';
8244   (void) ConcatenateMagickString(libpng_runv,
8245          png_get_libpng_ver(NULL),32);
8246
8247   *zlib_vers='\0';
8248   (void) ConcatenateMagickString(zlib_vers,
8249          ZLIB_VERSION,32);
8250   *zlib_runv='\0';
8251   (void) ConcatenateMagickString(zlib_runv,
8252          zlib_version,32);
8253
8254   if (logging != MagickFalse)
8255     {
8256        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
8257            im_vers);
8258        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8259            libpng_vers);
8260        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8261        {
8262        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8263            libpng_runv);
8264        }
8265        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8266            zlib_vers);
8267        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8268        {
8269        (void) LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8270            zlib_runv);
8271        }
8272     }
8273
8274   /* Initialize some stuff */
8275   ping_bit_depth=0,
8276   ping_color_type=0,
8277   ping_interlace_method=0,
8278   ping_compression_method=0,
8279   ping_filter_method=0,
8280   ping_num_trans = 0;
8281
8282   ping_background.red = 0;
8283   ping_background.green = 0;
8284   ping_background.blue = 0;
8285   ping_background.gray = 0;
8286   ping_background.index = 0;
8287
8288   ping_trans_color.red=0;
8289   ping_trans_color.green=0;
8290   ping_trans_color.blue=0;
8291   ping_trans_color.gray=0;
8292
8293   ping_pHYs_unit_type = 0;
8294   ping_pHYs_x_resolution = 0;
8295   ping_pHYs_y_resolution = 0;
8296
8297   ping_have_blob=MagickFalse;
8298   ping_have_cheap_transparency=MagickFalse;
8299   ping_have_color=MagickTrue;
8300   ping_have_non_bw=MagickTrue;
8301   ping_have_PLTE=MagickFalse;
8302   ping_have_bKGD=MagickFalse;
8303   ping_have_eXIf=MagickTrue;
8304   ping_have_iCCP=MagickFalse;
8305   ping_have_pHYs=MagickFalse;
8306   ping_have_sRGB=MagickFalse;
8307   ping_have_tRNS=MagickFalse;
8308
8309   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8310   ping_exclude_caNv=mng_info->ping_exclude_caNv;
8311   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8312   ping_exclude_date=mng_info->ping_exclude_date;
8313   ping_exclude_eXIf=mng_info->ping_exclude_eXIf;
8314   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8315   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8316   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8317   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8318   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8319   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8320   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8321   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8322   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8323   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8324   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8325   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8326
8327   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8328   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8329   ping_need_colortype_warning = MagickFalse;
8330
8331   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8332    * i.e., eliminate the ICC profile and set image->rendering_intent.
8333    * Note that this will not involve any changes to the actual pixels
8334    * but merely passes information to applications that read the resulting
8335    * PNG image.
8336    *
8337    * To do: recognize other variants of the sRGB profile, using the CRC to
8338    * verify all recognized variants including the 7 already known.
8339    *
8340    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8341    *
8342    * Use something other than image->rendering_intent to record the fact
8343    * that the sRGB profile was found.
8344    *
8345    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8346    * profile.  Record the Blackpoint Compensation, if any.
8347    */
8348    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8349    {
8350       char
8351         *name;
8352
8353       const StringInfo
8354         *profile;
8355
8356       ResetImageProfileIterator(image);
8357       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8358       {
8359         profile=GetImageProfile(image,name);
8360
8361         if (profile != (StringInfo *) NULL)
8362           {
8363             if ((LocaleCompare(name,"ICC") == 0) ||
8364                 (LocaleCompare(name,"ICM") == 0))
8365
8366              {
8367                  int
8368                    icheck,
8369                    got_crc=0;
8370
8371
8372                  png_uint_32
8373                    length,
8374                    profile_crc=0;
8375
8376                  unsigned char
8377                    *data;
8378
8379                  length=(png_uint_32) GetStringInfoLength(profile);
8380
8381                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8382                  {
8383                    if (length == sRGB_info[icheck].len)
8384                    {
8385                      if (got_crc == 0)
8386                      {
8387                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8388                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8389                          (unsigned long) length);
8390
8391                        data=GetStringInfoDatum(profile);
8392                        profile_crc=crc32(0,data,length);
8393
8394                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8395                            "      with crc=%8x",(unsigned int) profile_crc);
8396                        got_crc++;
8397                      }
8398
8399                      if (profile_crc == sRGB_info[icheck].crc)
8400                      {
8401                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8402                             "      It is sRGB with rendering intent = %s",
8403                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8404                              sRGB_info[icheck].intent));
8405                         if (image->rendering_intent==UndefinedIntent)
8406                         {
8407                           image->rendering_intent=
8408                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8409                              sRGB_info[icheck].intent);
8410                         }
8411                         ping_exclude_iCCP = MagickTrue;
8412                         ping_exclude_zCCP = MagickTrue;
8413                         ping_have_sRGB = MagickTrue;
8414                         break;
8415                      }
8416                    }
8417                  }
8418                  if (sRGB_info[icheck].len == 0)
8419                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8420                         "    Got %lu-byte ICC profile not recognized as sRGB",
8421                         (unsigned long) length);
8422               }
8423           }
8424         name=GetNextImageProfile(image);
8425       }
8426   }
8427
8428   number_opaque = 0;
8429   number_semitransparent = 0;
8430   number_transparent = 0;
8431
8432   if (logging != MagickFalse)
8433     {
8434       if (image->storage_class == UndefinedClass)
8435           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8436           "    image->storage_class=UndefinedClass");
8437       if (image->storage_class == DirectClass)
8438           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8439           "    image->storage_class=DirectClass");
8440       if (image->storage_class == PseudoClass)
8441           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8442           "    image->storage_class=PseudoClass");
8443       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8444           "    image->taint=MagickTrue":
8445           "    image->taint=MagickFalse");
8446     }
8447
8448   if (image->storage_class == PseudoClass &&
8449      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8450      mng_info->write_png48 || mng_info->write_png64 ||
8451      (mng_info->write_png_colortype != 1 &&
8452      mng_info->write_png_colortype != 5)))
8453     {
8454       (void) SyncImage(image,exception);
8455       image->storage_class = DirectClass;
8456     }
8457
8458   if (ping_preserve_colormap == MagickFalse)
8459     {
8460       if (image->storage_class != PseudoClass && image->colormap != NULL)
8461         {
8462           /* Free the bogus colormap; it can cause trouble later */
8463            if (logging != MagickFalse)
8464               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8465               "    Freeing bogus colormap");
8466            (void) RelinquishMagickMemory(image->colormap);
8467            image->colormap=NULL;
8468         }
8469     }
8470
8471   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8472     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8473
8474   /*
8475     Sometimes we get PseudoClass images whose RGB values don't match
8476     the colors in the colormap.  This code syncs the RGB values.
8477   */
8478   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8479      (void) SyncImage(image,exception);
8480
8481 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8482   if (image->depth > 8)
8483     {
8484       if (logging != MagickFalse)
8485         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8486           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8487
8488       image->depth=8;
8489     }
8490 #endif
8491
8492   /* Respect the -depth option */
8493   if (image->depth < 4)
8494     {
8495        register Quantum
8496          *r;
8497
8498        if (image->depth > 2)
8499          {
8500            /* Scale to 4-bit */
8501            LBR04PacketRGBO(image->background_color);
8502
8503            for (y=0; y < (ssize_t) image->rows; y++)
8504            {
8505              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8506
8507              if (r == (Quantum *) NULL)
8508                break;
8509
8510              for (x=0; x < (ssize_t) image->columns; x++)
8511              {
8512                 LBR04PixelRGBA(r);
8513                 r+=GetPixelChannels(image);
8514              }
8515
8516              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8517                 break;
8518            }
8519
8520            if (image->storage_class == PseudoClass && image->colormap != NULL)
8521            {
8522              for (i=0; i < (ssize_t) image->colors; i++)
8523              {
8524                LBR04PacketRGBO(image->colormap[i]);
8525              }
8526            }
8527          }
8528        else if (image->depth > 1)
8529          {
8530            /* Scale to 2-bit */
8531            LBR02PacketRGBO(image->background_color);
8532
8533            for (y=0; y < (ssize_t) image->rows; y++)
8534            {
8535              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8536
8537              if (r == (Quantum *) NULL)
8538                break;
8539
8540              for (x=0; x < (ssize_t) image->columns; x++)
8541              {
8542                 LBR02PixelRGBA(r);
8543                 r+=GetPixelChannels(image);
8544              }
8545
8546              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8547                 break;
8548            }
8549
8550            if (image->storage_class == PseudoClass && image->colormap != NULL)
8551            {
8552              for (i=0; i < (ssize_t) image->colors; i++)
8553              {
8554                LBR02PacketRGBO(image->colormap[i]);
8555              }
8556            }
8557          }
8558        else
8559          {
8560            /* Scale to 1-bit */
8561            LBR01PacketRGBO(image->background_color);
8562
8563            for (y=0; y < (ssize_t) image->rows; y++)
8564            {
8565              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8566
8567              if (r == (Quantum *) NULL)
8568                break;
8569
8570              for (x=0; x < (ssize_t) image->columns; x++)
8571              {
8572                 LBR01PixelRGBA(r);
8573                 r+=GetPixelChannels(image);
8574              }
8575
8576              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8577                 break;
8578            }
8579
8580            if (image->storage_class == PseudoClass && image->colormap != NULL)
8581            {
8582              for (i=0; i < (ssize_t) image->colors; i++)
8583              {
8584                LBR01PacketRGBO(image->colormap[i]);
8585              }
8586            }
8587          }
8588     }
8589
8590   /* To do: set to next higher multiple of 8 */
8591   if (image->depth < 8)
8592      image->depth=8;
8593
8594 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8595   /* PNG does not handle depths greater than 16 so reduce it even
8596    * if lossy
8597    */
8598   if (image->depth > 8)
8599       image->depth=16;
8600 #endif
8601
8602 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8603   if (image->depth > 8)
8604     {
8605       /* To do: fill low byte properly */
8606       image->depth=16;
8607     }
8608
8609   if (image->depth == 16 && mng_info->write_png_depth != 16)
8610     if (mng_info->write_png8 ||
8611         LosslessReduceDepthOK(image,exception) != MagickFalse)
8612       image->depth = 8;
8613 #endif
8614
8615   image_colors = (int) image->colors;
8616   number_opaque = (int) image->colors;
8617   number_transparent = 0;
8618   number_semitransparent = 0;
8619
8620   if (mng_info->write_png_colortype &&
8621      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8622      mng_info->write_png_colortype < 4 &&
8623      image->alpha_trait == UndefinedPixelTrait)))
8624   {
8625      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8626       * are not going to need the result.
8627       */
8628      if (mng_info->write_png_colortype == 1 ||
8629         mng_info->write_png_colortype == 5)
8630        ping_have_color=MagickFalse;
8631
8632      if (image->alpha_trait != UndefinedPixelTrait)
8633        {
8634          number_transparent = 2;
8635          number_semitransparent = 1;
8636        }
8637   }
8638
8639   if (mng_info->write_png_colortype < 7)
8640   {
8641   /* BUILD_PALETTE
8642    *
8643    * Normally we run this just once, but in the case of writing PNG8
8644    * we reduce the transparency to binary and run again, then if there
8645    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8646    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8647    * palette.  Then (To do) we take care of a final reduction that is only
8648    * needed if there are still 256 colors present and one of them has both
8649    * transparent and opaque instances.
8650    */
8651
8652   tried_332 = MagickFalse;
8653   tried_333 = MagickFalse;
8654   tried_444 = MagickFalse;
8655
8656   for (j=0; j<6; j++)
8657   {
8658     /*
8659      * Sometimes we get DirectClass images that have 256 colors or fewer.
8660      * This code will build a colormap.
8661      *
8662      * Also, sometimes we get PseudoClass images with an out-of-date
8663      * colormap.  This code will replace the colormap with a new one.
8664      * Sometimes we get PseudoClass images that have more than 256 colors.
8665      * This code will delete the colormap and change the image to
8666      * DirectClass.
8667      *
8668      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8669      * even though it sometimes contains left-over non-opaque values.
8670      *
8671      * Also we gather some information (number of opaque, transparent,
8672      * and semitransparent pixels, and whether the image has any non-gray
8673      * pixels or only black-and-white pixels) that we might need later.
8674      *
8675      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8676      * we need to check for bogus non-opaque values, at least.
8677      */
8678
8679    int
8680      n;
8681
8682    PixelInfo
8683      opaque[260],
8684      semitransparent[260],
8685      transparent[260];
8686
8687    register const Quantum
8688      *s;
8689
8690    register Quantum
8691      *q,
8692      *r;
8693
8694    if (logging != MagickFalse)
8695      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8696          "    Enter BUILD_PALETTE:");
8697
8698    if (logging != MagickFalse)
8699      {
8700        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8701              "      image->columns=%.20g",(double) image->columns);
8702        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8703              "      image->rows=%.20g",(double) image->rows);
8704        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8705              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8706        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8707              "      image->depth=%.20g",(double) image->depth);
8708
8709        if (image->storage_class == PseudoClass && image->colormap != NULL)
8710        {
8711          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8712              "      Original colormap:");
8713          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8714              "        i    (red,green,blue,alpha)");
8715
8716          for (i=0; i < 256; i++)
8717          {
8718                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8719                    "        %d    (%d,%d,%d,%d)",
8720                     (int) i,
8721                     (int) image->colormap[i].red,
8722                     (int) image->colormap[i].green,
8723                     (int) image->colormap[i].blue,
8724                     (int) image->colormap[i].alpha);
8725          }
8726
8727          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8728          {
8729            if (i > 255)
8730              {
8731                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8732                    "        %d    (%d,%d,%d,%d)",
8733                     (int) i,
8734                     (int) image->colormap[i].red,
8735                     (int) image->colormap[i].green,
8736                     (int) image->colormap[i].blue,
8737                     (int) image->colormap[i].alpha);
8738              }
8739          }
8740        }
8741
8742        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8743            "      image->colors=%d",(int) image->colors);
8744
8745        if (image->colors == 0)
8746          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8747              "        (zero means unknown)");
8748
8749        if (ping_preserve_colormap == MagickFalse)
8750          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8751               "      Regenerate the colormap");
8752      }
8753
8754      image_colors=0;
8755      number_opaque = 0;
8756      number_semitransparent = 0;
8757      number_transparent = 0;
8758
8759      for (y=0; y < (ssize_t) image->rows; y++)
8760      {
8761        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8762
8763        if (q == (Quantum *) NULL)
8764          break;
8765
8766        for (x=0; x < (ssize_t) image->columns; x++)
8767        {
8768            if (image->alpha_trait == UndefinedPixelTrait ||
8769               GetPixelAlpha(image,q) == OpaqueAlpha)
8770              {
8771                if (number_opaque < 259)
8772                  {
8773                    if (number_opaque == 0)
8774                      {
8775                        GetPixelInfoPixel(image, q, opaque);
8776                        opaque[0].alpha=OpaqueAlpha;
8777                        number_opaque=1;
8778                      }
8779
8780                    for (i=0; i< (ssize_t) number_opaque; i++)
8781                      {
8782                        if (Magick_png_color_equal(image,q,opaque+i))
8783                          break;
8784                      }
8785
8786                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8787                      {
8788                        number_opaque++;
8789                        GetPixelInfoPixel(image, q, opaque+i);
8790                        opaque[i].alpha=OpaqueAlpha;
8791                      }
8792                  }
8793              }
8794            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8795              {
8796                if (number_transparent < 259)
8797                  {
8798                    if (number_transparent == 0)
8799                      {
8800                        GetPixelInfoPixel(image, q, transparent);
8801                        ping_trans_color.red=(unsigned short)
8802                          GetPixelRed(image,q);
8803                        ping_trans_color.green=(unsigned short)
8804                          GetPixelGreen(image,q);
8805                        ping_trans_color.blue=(unsigned short)
8806                          GetPixelBlue(image,q);
8807                        ping_trans_color.gray=(unsigned short)
8808                          GetPixelGray(image,q);
8809                        number_transparent = 1;
8810                      }
8811
8812                    for (i=0; i< (ssize_t) number_transparent; i++)
8813                      {
8814                        if (Magick_png_color_equal(image,q,transparent+i))
8815                          break;
8816                      }
8817
8818                    if (i ==  (ssize_t) number_transparent &&
8819                        number_transparent < 259)
8820                      {
8821                        number_transparent++;
8822                        GetPixelInfoPixel(image,q,transparent+i);
8823                      }
8824                  }
8825              }
8826            else
8827              {
8828                if (number_semitransparent < 259)
8829                  {
8830                    if (number_semitransparent == 0)
8831                      {
8832                        GetPixelInfoPixel(image,q,semitransparent);
8833                        number_semitransparent = 1;
8834                      }
8835
8836                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8837                      {
8838                        if (Magick_png_color_equal(image,q,semitransparent+i)
8839                            && GetPixelAlpha(image,q) ==
8840                            semitransparent[i].alpha)
8841                          break;
8842                      }
8843
8844                    if (i ==  (ssize_t) number_semitransparent &&
8845                        number_semitransparent < 259)
8846                      {
8847                        number_semitransparent++;
8848                        GetPixelInfoPixel(image, q, semitransparent+i);
8849                      }
8850                  }
8851              }
8852            q+=GetPixelChannels(image);
8853         }
8854      }
8855
8856      if (mng_info->write_png8 == MagickFalse &&
8857          ping_exclude_bKGD == MagickFalse)
8858        {
8859          /* Add the background color to the palette, if it
8860           * isn't already there.
8861           */
8862           if (logging != MagickFalse)
8863             {
8864               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8865                   "      Check colormap for background (%d,%d,%d)",
8866                   (int) image->background_color.red,
8867                   (int) image->background_color.green,
8868                   (int) image->background_color.blue);
8869             }
8870           for (i=0; i<number_opaque; i++)
8871           {
8872              if (opaque[i].red == image->background_color.red &&
8873                  opaque[i].green == image->background_color.green &&
8874                  opaque[i].blue == image->background_color.blue)
8875                break;
8876           }
8877           if (number_opaque < 259 && i == number_opaque)
8878             {
8879                opaque[i] = image->background_color;
8880                ping_background.index = i;
8881                number_opaque++;
8882                if (logging != MagickFalse)
8883                  {
8884                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8885                        "      background_color index is %d",(int) i);
8886                  }
8887
8888             }
8889           else if (logging != MagickFalse)
8890               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8891                   "      No room in the colormap to add background color");
8892        }
8893
8894      image_colors=number_opaque+number_transparent+number_semitransparent;
8895
8896      if (logging != MagickFalse)
8897        {
8898          if (image_colors > 256)
8899             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8900                   "      image has more than 256 colors");
8901
8902          else
8903             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8904                   "      image has %d colors",image_colors);
8905        }
8906
8907      if (ping_preserve_colormap != MagickFalse)
8908        break;
8909
8910      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8911        {
8912          ping_have_color=MagickFalse;
8913          ping_have_non_bw=MagickFalse;
8914
8915          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8916          {
8917            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8918               "incompatible colorspace");
8919            ping_have_color=MagickTrue;
8920            ping_have_non_bw=MagickTrue;
8921          }
8922
8923          if(image_colors > 256)
8924            {
8925              for (y=0; y < (ssize_t) image->rows; y++)
8926              {
8927                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8928
8929                if (q == (Quantum *) NULL)
8930                  break;
8931
8932                s=q;
8933                for (x=0; x < (ssize_t) image->columns; x++)
8934                {
8935                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8936                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8937                    {
8938                       ping_have_color=MagickTrue;
8939                       ping_have_non_bw=MagickTrue;
8940                       break;
8941                    }
8942                  s+=GetPixelChannels(image);
8943                }
8944
8945                if (ping_have_color != MagickFalse)
8946                  break;
8947
8948                /* Worst case is black-and-white; we are looking at every
8949                 * pixel twice.
8950                 */
8951
8952                if (ping_have_non_bw == MagickFalse)
8953                  {
8954                    s=q;
8955                    for (x=0; x < (ssize_t) image->columns; x++)
8956                    {
8957                      if (GetPixelRed(image,s) != 0 &&
8958                          GetPixelRed(image,s) != QuantumRange)
8959                        {
8960                          ping_have_non_bw=MagickTrue;
8961                          break;
8962                        }
8963                      s+=GetPixelChannels(image);
8964                    }
8965                }
8966              }
8967            }
8968        }
8969
8970      if (image_colors < 257)
8971        {
8972          PixelInfo
8973            colormap[260];
8974
8975          /*
8976           * Initialize image colormap.
8977           */
8978
8979          if (logging != MagickFalse)
8980             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8981                   "      Sort the new colormap");
8982
8983         /* Sort palette, transparent first */;
8984
8985          n = 0;
8986
8987          for (i=0; i<number_transparent; i++)
8988             colormap[n++] = transparent[i];
8989
8990          for (i=0; i<number_semitransparent; i++)
8991             colormap[n++] = semitransparent[i];
8992
8993          for (i=0; i<number_opaque; i++)
8994             colormap[n++] = opaque[i];
8995
8996          ping_background.index +=
8997            (number_transparent + number_semitransparent);
8998
8999          /* image_colors < 257; search the colormap instead of the pixels
9000           * to get ping_have_color and ping_have_non_bw
9001           */
9002          for (i=0; i<n; i++)
9003          {
9004            if (ping_have_color == MagickFalse)
9005              {
9006                 if (colormap[i].red != colormap[i].green ||
9007                     colormap[i].red != colormap[i].blue)
9008                   {
9009                      ping_have_color=MagickTrue;
9010                      ping_have_non_bw=MagickTrue;
9011                      break;
9012                   }
9013               }
9014
9015            if (ping_have_non_bw == MagickFalse)
9016              {
9017                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
9018                    ping_have_non_bw=MagickTrue;
9019              }
9020           }
9021
9022         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
9023             (number_transparent == 0 && number_semitransparent == 0)) &&
9024             (((mng_info->write_png_colortype-1) ==
9025             PNG_COLOR_TYPE_PALETTE) ||
9026             (mng_info->write_png_colortype == 0)))
9027           {
9028             if (logging != MagickFalse)
9029               {
9030                 if (n !=  (ssize_t) image_colors)
9031                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9032                    "   image_colors (%d) and n (%d)  don't match",
9033                    image_colors, n);
9034
9035                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9036                    "      AcquireImageColormap");
9037               }
9038
9039             image->colors = image_colors;
9040
9041             if (AcquireImageColormap(image,image_colors,exception) ==
9042                 MagickFalse)
9043                ThrowWriterException(ResourceLimitError,
9044                    "MemoryAllocationFailed");
9045
9046             for (i=0; i< (ssize_t) image_colors; i++)
9047                image->colormap[i] = colormap[i];
9048
9049             if (logging != MagickFalse)
9050               {
9051                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052                       "      image->colors=%d (%d)",
9053                       (int) image->colors, image_colors);
9054
9055                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9056                       "      Update the pixel indexes");
9057               }
9058
9059             /* Sync the pixel indices with the new colormap */
9060
9061             for (y=0; y < (ssize_t) image->rows; y++)
9062             {
9063               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9064
9065               if (q == (Quantum *) NULL)
9066                 break;
9067
9068               for (x=0; x < (ssize_t) image->columns; x++)
9069               {
9070                 for (i=0; i< (ssize_t) image_colors; i++)
9071                 {
9072                   if ((image->alpha_trait == UndefinedPixelTrait ||
9073                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
9074                       image->colormap[i].red == GetPixelRed(image,q) &&
9075                       image->colormap[i].green == GetPixelGreen(image,q) &&
9076                       image->colormap[i].blue == GetPixelBlue(image,q))
9077                   {
9078                     SetPixelIndex(image,i,q);
9079                     break;
9080                   }
9081                 }
9082                 q+=GetPixelChannels(image);
9083               }
9084
9085               if (SyncAuthenticPixels(image,exception) == MagickFalse)
9086                  break;
9087             }
9088           }
9089        }
9090
9091      if (logging != MagickFalse)
9092        {
9093          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9094             "      image->colors=%d", (int) image->colors);
9095
9096          if (image->colormap != NULL)
9097            {
9098              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9099                  "       i     (red,green,blue,alpha)");
9100
9101              for (i=0; i < (ssize_t) image->colors; i++)
9102              {
9103                if (i < 300 || i >= (ssize_t) image->colors - 10)
9104                  {
9105                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9106                        "       %d     (%d,%d,%d,%d)",
9107                         (int) i,
9108                         (int) image->colormap[i].red,
9109                         (int) image->colormap[i].green,
9110                         (int) image->colormap[i].blue,
9111                         (int) image->colormap[i].alpha);
9112                  }
9113              }
9114            }
9115
9116            if (number_transparent < 257)
9117              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9118                    "      number_transparent     = %d",
9119                    number_transparent);
9120            else
9121
9122              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9123                    "      number_transparent     > 256");
9124
9125            if (number_opaque < 257)
9126              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9127                    "      number_opaque          = %d",
9128                    number_opaque);
9129
9130            else
9131              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9132                    "      number_opaque          > 256");
9133
9134            if (number_semitransparent < 257)
9135              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9136                    "      number_semitransparent = %d",
9137                    number_semitransparent);
9138
9139            else
9140              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9141                    "      number_semitransparent > 256");
9142
9143            if (ping_have_non_bw == MagickFalse)
9144               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9145                     "      All pixels and the background are black or white");
9146
9147            else if (ping_have_color == MagickFalse)
9148               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9149                     "      All pixels and the background are gray");
9150
9151            else
9152               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9153                     "      At least one pixel or the background is non-gray");
9154
9155            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9156                "    Exit BUILD_PALETTE:");
9157        }
9158
9159    if (mng_info->write_png8 == MagickFalse)
9160       break;
9161
9162    /* Make any reductions necessary for the PNG8 format */
9163     if (image_colors <= 256 &&
9164         image_colors != 0 && image->colormap != NULL &&
9165         number_semitransparent == 0 &&
9166         number_transparent <= 1)
9167       break;
9168
9169     /* PNG8 can't have semitransparent colors so we threshold the
9170      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
9171      * transparent color so if more than one is transparent we merge
9172      * them into image->background_color.
9173      */
9174     if (number_semitransparent != 0 || number_transparent > 1)
9175       {
9176         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9177             "    Thresholding the alpha channel to binary");
9178
9179         for (y=0; y < (ssize_t) image->rows; y++)
9180         {
9181           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9182
9183           if (r == (Quantum *) NULL)
9184             break;
9185
9186           for (x=0; x < (ssize_t) image->columns; x++)
9187           {
9188               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
9189                 {
9190                   SetPixelViaPixelInfo(image,&image->background_color,r);
9191                   SetPixelAlpha(image,TransparentAlpha,r);
9192                 }
9193               else
9194                   SetPixelAlpha(image,OpaqueAlpha,r);
9195               r+=GetPixelChannels(image);
9196           }
9197
9198           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9199              break;
9200
9201           if (image_colors != 0 && image_colors <= 256 &&
9202              image->colormap != NULL)
9203             for (i=0; i<image_colors; i++)
9204                 image->colormap[i].alpha =
9205                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9206                     TransparentAlpha : OpaqueAlpha);
9207         }
9208       continue;
9209     }
9210
9211     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9212      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9213      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9214      * colors or less.
9215      */
9216     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9217       {
9218         if (logging != MagickFalse)
9219            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9220                "    Quantizing the background color to 4-4-4");
9221
9222         tried_444 = MagickTrue;
9223
9224         LBR04PacketRGB(image->background_color);
9225
9226         if (logging != MagickFalse)
9227           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9228               "    Quantizing the pixel colors to 4-4-4");
9229
9230         if (image->colormap == NULL)
9231         {
9232           for (y=0; y < (ssize_t) image->rows; y++)
9233           {
9234             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9235
9236             if (r == (Quantum *) NULL)
9237               break;
9238
9239             for (x=0; x < (ssize_t) image->columns; x++)
9240             {
9241               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9242                   LBR04PixelRGB(r);
9243               r+=GetPixelChannels(image);
9244             }
9245
9246             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9247                break;
9248           }
9249         }
9250
9251         else /* Should not reach this; colormap already exists and
9252                 must be <= 256 */
9253         {
9254           if (logging != MagickFalse)
9255               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9256               "    Quantizing the colormap to 4-4-4");
9257
9258           for (i=0; i<image_colors; i++)
9259           {
9260             LBR04PacketRGB(image->colormap[i]);
9261           }
9262         }
9263         continue;
9264       }
9265
9266     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9267       {
9268         if (logging != MagickFalse)
9269            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9270                "    Quantizing the background color to 3-3-3");
9271
9272         tried_333 = MagickTrue;
9273
9274         LBR03PacketRGB(image->background_color);
9275
9276         if (logging != MagickFalse)
9277           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9278               "    Quantizing the pixel colors to 3-3-3-1");
9279
9280         if (image->colormap == NULL)
9281         {
9282           for (y=0; y < (ssize_t) image->rows; y++)
9283           {
9284             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9285
9286             if (r == (Quantum *) NULL)
9287               break;
9288
9289             for (x=0; x < (ssize_t) image->columns; x++)
9290             {
9291               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9292                   LBR03RGB(r);
9293               r+=GetPixelChannels(image);
9294             }
9295
9296             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9297                break;
9298           }
9299         }
9300
9301         else /* Should not reach this; colormap already exists and
9302                 must be <= 256 */
9303         {
9304           if (logging != MagickFalse)
9305               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9306               "    Quantizing the colormap to 3-3-3-1");
9307           for (i=0; i<image_colors; i++)
9308           {
9309               LBR03PacketRGB(image->colormap[i]);
9310           }
9311         }
9312         continue;
9313       }
9314
9315     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9316       {
9317         if (logging != MagickFalse)
9318            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9319                "    Quantizing the background color to 3-3-2");
9320
9321         tried_332 = MagickTrue;
9322
9323         /* Red and green were already done so we only quantize the blue
9324          * channel
9325          */
9326
9327         LBR02PacketBlue(image->background_color);
9328
9329         if (logging != MagickFalse)
9330           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9331               "    Quantizing the pixel colors to 3-3-2-1");
9332
9333         if (image->colormap == NULL)
9334         {
9335           for (y=0; y < (ssize_t) image->rows; y++)
9336           {
9337             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9338
9339             if (r == (Quantum *) NULL)
9340               break;
9341
9342             for (x=0; x < (ssize_t) image->columns; x++)
9343             {
9344               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9345                   LBR02PixelBlue(r);
9346               r+=GetPixelChannels(image);
9347             }
9348
9349             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9350                break;
9351           }
9352         }
9353
9354         else /* Should not reach this; colormap already exists and
9355                 must be <= 256 */
9356         {
9357           if (logging != MagickFalse)
9358               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9359               "    Quantizing the colormap to 3-3-2-1");
9360           for (i=0; i<image_colors; i++)
9361           {
9362               LBR02PacketBlue(image->colormap[i]);
9363           }
9364       }
9365       continue;
9366     }
9367
9368     if (image_colors == 0 || image_colors > 256)
9369     {
9370       /* Take care of special case with 256 opaque colors + 1 transparent
9371        * color.  We don't need to quantize to 2-3-2-1; we only need to
9372        * eliminate one color, so we'll merge the two darkest red
9373        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9374        */
9375       if (logging != MagickFalse)
9376         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9377             "    Merging two dark red background colors to 3-3-2-1");
9378
9379       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9380           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9381           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9382       {
9383          image->background_color.red=ScaleCharToQuantum(0x24);
9384       }
9385
9386       if (logging != MagickFalse)
9387         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9388             "    Merging two dark red pixel colors to 3-3-2-1");
9389
9390       if (image->colormap == NULL)
9391       {
9392         for (y=0; y < (ssize_t) image->rows; y++)
9393         {
9394           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9395
9396           if (r == (Quantum *) NULL)
9397             break;
9398
9399           for (x=0; x < (ssize_t) image->columns; x++)
9400           {
9401             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9402                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9403                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9404                 GetPixelAlpha(image,r) == OpaqueAlpha)
9405               {
9406                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9407               }
9408             r+=GetPixelChannels(image);
9409           }
9410
9411           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9412              break;
9413
9414         }
9415       }
9416
9417       else
9418       {
9419          for (i=0; i<image_colors; i++)
9420          {
9421             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9422                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9423                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9424             {
9425                image->colormap[i].red=ScaleCharToQuantum(0x24);
9426             }
9427          }
9428       }
9429     }
9430   }
9431   }
9432   /* END OF BUILD_PALETTE */
9433
9434   /* If we are excluding the tRNS chunk and there is transparency,
9435    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9436    * PNG.
9437    */
9438   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9439      (number_transparent != 0 || number_semitransparent != 0))
9440     {
9441       unsigned int colortype=mng_info->write_png_colortype;
9442
9443       if (ping_have_color == MagickFalse)
9444         mng_info->write_png_colortype = 5;
9445
9446       else
9447         mng_info->write_png_colortype = 7;
9448
9449       if (colortype != 0 &&
9450          mng_info->write_png_colortype != colortype)
9451         ping_need_colortype_warning=MagickTrue;
9452
9453     }
9454
9455   /* See if cheap transparency is possible.  It is only possible
9456    * when there is a single transparent color, no semitransparent
9457    * color, and no opaque color that has the same RGB components
9458    * as the transparent color.  We only need this information if
9459    * we are writing a PNG with colortype 0 or 2, and we have not
9460    * excluded the tRNS chunk.
9461    */
9462   if (number_transparent == 1 &&
9463       mng_info->write_png_colortype < 4)
9464     {
9465        ping_have_cheap_transparency = MagickTrue;
9466
9467        if (number_semitransparent != 0)
9468          ping_have_cheap_transparency = MagickFalse;
9469
9470        else if (image_colors == 0 || image_colors > 256 ||
9471            image->colormap == NULL)
9472          {
9473            register const Quantum
9474              *q;
9475
9476            for (y=0; y < (ssize_t) image->rows; y++)
9477            {
9478              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9479
9480              if (q == (Quantum *) NULL)
9481                break;
9482
9483              for (x=0; x < (ssize_t) image->columns; x++)
9484              {
9485                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9486                      (unsigned short) GetPixelRed(image,q) ==
9487                                      ping_trans_color.red &&
9488                      (unsigned short) GetPixelGreen(image,q) ==
9489                                      ping_trans_color.green &&
9490                      (unsigned short) GetPixelBlue(image,q) ==
9491                                      ping_trans_color.blue)
9492                    {
9493                      ping_have_cheap_transparency = MagickFalse;
9494                      break;
9495                    }
9496
9497                  q+=GetPixelChannels(image);
9498              }
9499
9500              if (ping_have_cheap_transparency == MagickFalse)
9501                 break;
9502            }
9503          }
9504        else
9505          {
9506             /* Assuming that image->colormap[0] is the one transparent color
9507              * and that all others are opaque.
9508              */
9509             if (image_colors > 1)
9510               for (i=1; i<image_colors; i++)
9511                 if (image->colormap[i].red == image->colormap[0].red &&
9512                     image->colormap[i].green == image->colormap[0].green &&
9513                     image->colormap[i].blue == image->colormap[0].blue)
9514                   {
9515                      ping_have_cheap_transparency = MagickFalse;
9516                      break;
9517                   }
9518          }
9519
9520        if (logging != MagickFalse)
9521          {
9522            if (ping_have_cheap_transparency == MagickFalse)
9523              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9524                  "   Cheap transparency is not possible.");
9525
9526            else
9527              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9528                  "   Cheap transparency is possible.");
9529          }
9530      }
9531   else
9532     ping_have_cheap_transparency = MagickFalse;
9533
9534   image_depth=image->depth;
9535
9536   quantum_info = (QuantumInfo *) NULL;
9537   number_colors=0;
9538   image_colors=(int) image->colors;
9539   image_matte=image->alpha_trait !=
9540         UndefinedPixelTrait ? MagickTrue : MagickFalse;
9541
9542   if (mng_info->write_png_colortype < 5)
9543     mng_info->IsPalette=image->storage_class == PseudoClass &&
9544       image_colors <= 256 && image->colormap != NULL;
9545   else
9546     mng_info->IsPalette = MagickFalse;
9547
9548   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9549      (image->colors == 0 || image->colormap == NULL))
9550     {
9551       image_info=DestroyImageInfo(image_info);
9552       image=DestroyImage(image);
9553       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9554           "Cannot write PNG8 or color-type 3; colormap is NULL",
9555           "`%s'",IMimage->filename);
9556       return(MagickFalse);
9557     }
9558
9559   /*
9560     Allocate the PNG structures
9561   */
9562 #ifdef PNG_USER_MEM_SUPPORTED
9563  error_info.image=image;
9564  error_info.exception=exception;
9565   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9566     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9567     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9568
9569 #else
9570   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9571     MagickPNGErrorHandler,MagickPNGWarningHandler);
9572
9573 #endif
9574   if (ping == (png_struct *) NULL)
9575     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9576
9577   ping_info=png_create_info_struct(ping);
9578
9579   if (ping_info == (png_info *) NULL)
9580     {
9581       png_destroy_write_struct(&ping,(png_info **) NULL);
9582       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9583     }
9584
9585   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9586   pixel_info=(MemoryInfo *) NULL;
9587
9588   if (setjmp(png_jmpbuf(ping)))
9589     {
9590       /*
9591         PNG write failed.
9592       */
9593 #ifdef PNG_DEBUG
9594      if (image_info->verbose)
9595         (void) printf("PNG write has failed.\n");
9596 #endif
9597       png_destroy_write_struct(&ping,&ping_info);
9598 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9599       UnlockSemaphoreInfo(ping_semaphore);
9600 #endif
9601
9602       if (pixel_info != (MemoryInfo *) NULL)
9603         pixel_info=RelinquishVirtualMemory(pixel_info);
9604
9605       if (quantum_info != (QuantumInfo *) NULL)
9606         quantum_info=DestroyQuantumInfo(quantum_info);
9607
9608       if (ping_have_blob != MagickFalse)
9609           (void) CloseBlob(image);
9610       image_info=DestroyImageInfo(image_info);
9611       image=DestroyImage(image);
9612       return(MagickFalse);
9613     }
9614
9615   /* {  For navigation to end of SETJMP-protected block.  Within this
9616    *    block, use png_error() instead of Throwing an Exception, to ensure
9617    *    that libpng is able to clean up, and that the semaphore is unlocked.
9618    */
9619
9620 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9621   LockSemaphoreInfo(ping_semaphore);
9622 #endif
9623
9624 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9625   /* Allow benign errors */
9626   png_set_benign_errors(ping, 1);
9627 #endif
9628
9629 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9630   /* Reject images with too many rows or columns */
9631   png_set_user_limits(ping,
9632     (png_uint_32) MagickMin(0x7fffffffL,
9633         GetMagickResourceLimit(WidthResource)),
9634     (png_uint_32) MagickMin(0x7fffffffL,
9635         GetMagickResourceLimit(HeightResource)));
9636 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9637
9638   /*
9639     Prepare PNG for writing.
9640   */
9641
9642 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9643   if (mng_info->write_mng)
9644   {
9645      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9646 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9647      /* Disable new libpng-1.5.10 feature when writing a MNG because
9648       * zero-length PLTE is OK
9649       */
9650      png_set_check_for_invalid_index (ping, 0);
9651 # endif
9652   }
9653
9654 #else
9655 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9656   if (mng_info->write_mng)
9657      png_permit_empty_plte(ping,MagickTrue);
9658
9659 # endif
9660 #endif
9661
9662   x=0;
9663
9664   ping_width=(png_uint_32) image->columns;
9665   ping_height=(png_uint_32) image->rows;
9666
9667   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9668      image_depth=8;
9669
9670   if (mng_info->write_png48 || mng_info->write_png64)
9671      image_depth=16;
9672
9673   if (mng_info->write_png_depth != 0)
9674      image_depth=mng_info->write_png_depth;
9675
9676   /* Adjust requested depth to next higher valid depth if necessary */
9677   if (image_depth > 8)
9678      image_depth=16;
9679
9680   if ((image_depth > 4) && (image_depth < 8))
9681      image_depth=8;
9682
9683   if (image_depth == 3)
9684      image_depth=4;
9685
9686   if (logging != MagickFalse)
9687     {
9688      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9689         "    width=%.20g",(double) ping_width);
9690      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9691         "    height=%.20g",(double) ping_height);
9692      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9693         "    image_matte=%.20g",(double) image->alpha_trait);
9694      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9695         "    image->depth=%.20g",(double) image->depth);
9696      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9697         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9698     }
9699
9700   save_image_depth=image_depth;
9701   ping_bit_depth=(png_byte) save_image_depth;
9702
9703
9704 #if defined(PNG_pHYs_SUPPORTED)
9705   if (ping_exclude_pHYs == MagickFalse)
9706   {
9707   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9708       (!mng_info->write_mng || !mng_info->equal_physs))
9709     {
9710       if (logging != MagickFalse)
9711         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9712             "    Setting up pHYs chunk");
9713
9714       if (image->units == PixelsPerInchResolution)
9715         {
9716           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9717           ping_pHYs_x_resolution=
9718              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9719           ping_pHYs_y_resolution=
9720              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9721         }
9722
9723       else if (image->units == PixelsPerCentimeterResolution)
9724         {
9725           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9726           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9727           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9728         }
9729
9730       else
9731         {
9732           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9733           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9734           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9735         }
9736
9737       if (logging != MagickFalse)
9738         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9739           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9740           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9741           (int) ping_pHYs_unit_type);
9742        ping_have_pHYs = MagickTrue;
9743     }
9744   }
9745 #endif
9746
9747   if (ping_exclude_bKGD == MagickFalse)
9748   {
9749   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9750     {
9751        unsigned int
9752          mask;
9753
9754        mask=0xffff;
9755        if (ping_bit_depth == 8)
9756           mask=0x00ff;
9757
9758        if (ping_bit_depth == 4)
9759           mask=0x000f;
9760
9761        if (ping_bit_depth == 2)
9762           mask=0x0003;
9763
9764        if (ping_bit_depth == 1)
9765           mask=0x0001;
9766
9767        ping_background.red=(png_uint_16)
9768          (ScaleQuantumToShort(image->background_color.red) & mask);
9769
9770        ping_background.green=(png_uint_16)
9771          (ScaleQuantumToShort(image->background_color.green) & mask);
9772
9773        ping_background.blue=(png_uint_16)
9774          (ScaleQuantumToShort(image->background_color.blue) & mask);
9775
9776        ping_background.gray=(png_uint_16) ping_background.green;
9777     }
9778
9779   if (logging != MagickFalse)
9780     {
9781       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9782           "    Setting up bKGD chunk (1)");
9783       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9784           "      background_color index is %d",
9785           (int) ping_background.index);
9786
9787       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9788           "    ping_bit_depth=%d",ping_bit_depth);
9789     }
9790
9791   ping_have_bKGD = MagickTrue;
9792   }
9793
9794   /*
9795     Select the color type.
9796   */
9797   matte=image_matte;
9798   old_bit_depth=0;
9799
9800   if (mng_info->IsPalette && mng_info->write_png8)
9801     {
9802       /* To do: make this a function cause it's used twice, except
9803          for reducing the sample depth from 8. */
9804
9805       number_colors=image_colors;
9806
9807       ping_have_tRNS=MagickFalse;
9808
9809       /*
9810         Set image palette.
9811       */
9812       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9813
9814       if (logging != MagickFalse)
9815         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9816             "  Setting up PLTE chunk with %d colors (%d)",
9817             number_colors, image_colors);
9818
9819       for (i=0; i < (ssize_t) number_colors; i++)
9820       {
9821         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9822         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9823         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9824         if (logging != MagickFalse)
9825           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9826 #if MAGICKCORE_QUANTUM_DEPTH == 8
9827             "    %3ld (%3d,%3d,%3d)",
9828 #else
9829             "    %5ld (%5d,%5d,%5d)",
9830 #endif
9831             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9832
9833       }
9834
9835       ping_have_PLTE=MagickTrue;
9836       image_depth=ping_bit_depth;
9837       ping_num_trans=0;
9838
9839       if (matte != MagickFalse)
9840       {
9841           /*
9842             Identify which colormap entry is transparent.
9843           */
9844           assert(number_colors <= 256);
9845           assert(image->colormap != NULL);
9846
9847           for (i=0; i < (ssize_t) number_transparent; i++)
9848              ping_trans_alpha[i]=0;
9849
9850
9851           ping_num_trans=(unsigned short) (number_transparent +
9852              number_semitransparent);
9853
9854           if (ping_num_trans == 0)
9855              ping_have_tRNS=MagickFalse;
9856
9857           else
9858              ping_have_tRNS=MagickTrue;
9859       }
9860
9861       if (ping_exclude_bKGD == MagickFalse)
9862       {
9863        /*
9864         * Identify which colormap entry is the background color.
9865         */
9866
9867         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9868           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9869             break;
9870
9871         ping_background.index=(png_byte) i;
9872
9873         if (logging != MagickFalse)
9874           {
9875             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9876                  "      background_color index is %d",
9877                  (int) ping_background.index);
9878           }
9879       }
9880     } /* end of write_png8 */
9881
9882   else if (mng_info->write_png_colortype == 1)
9883     {
9884       image_matte=MagickFalse;
9885       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9886     }
9887
9888   else if (mng_info->write_png24 || mng_info->write_png48 ||
9889       mng_info->write_png_colortype == 3)
9890     {
9891       image_matte=MagickFalse;
9892       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9893     }
9894
9895   else if (mng_info->write_png32 || mng_info->write_png64 ||
9896       mng_info->write_png_colortype == 7)
9897     {
9898       image_matte=MagickTrue;
9899       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9900     }
9901
9902   else /* mng_info->write_pngNN not specified */
9903     {
9904       image_depth=ping_bit_depth;
9905
9906       if (mng_info->write_png_colortype != 0)
9907         {
9908           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9909
9910           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9911               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9912             image_matte=MagickTrue;
9913
9914           else
9915             image_matte=MagickFalse;
9916
9917           if (logging != MagickFalse)
9918              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9919              "   PNG colortype %d was specified:",(int) ping_color_type);
9920         }
9921
9922       else /* write_png_colortype not specified */
9923         {
9924           if (logging != MagickFalse)
9925              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9926              "  Selecting PNG colortype:");
9927
9928           ping_color_type=(png_byte) ((matte != MagickFalse)?
9929             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9930
9931           if (image_info->type == TrueColorType)
9932             {
9933               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9934               image_matte=MagickFalse;
9935             }
9936
9937           if (image_info->type == TrueColorAlphaType)
9938             {
9939               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9940               image_matte=MagickTrue;
9941             }
9942
9943           if (image_info->type == PaletteType ||
9944               image_info->type == PaletteAlphaType)
9945             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9946
9947           if (mng_info->write_png_colortype == 0 &&
9948              image_info->type == UndefinedType)
9949             {
9950               if (ping_have_color == MagickFalse)
9951                 {
9952                   if (image_matte == MagickFalse)
9953                     {
9954                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9955                       image_matte=MagickFalse;
9956                     }
9957
9958                   else
9959                     {
9960                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9961                       image_matte=MagickTrue;
9962                     }
9963                 }
9964               else
9965                 {
9966                   if (image_matte == MagickFalse)
9967                     {
9968                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9969                       image_matte=MagickFalse;
9970                     }
9971
9972                   else
9973                     {
9974                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9975                       image_matte=MagickTrue;
9976                     }
9977                  }
9978             }
9979
9980         }
9981
9982       if (logging != MagickFalse)
9983          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9984          "    Selected PNG colortype=%d",ping_color_type);
9985
9986       if (ping_bit_depth < 8)
9987         {
9988           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9989               ping_color_type == PNG_COLOR_TYPE_RGB ||
9990               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9991             ping_bit_depth=8;
9992         }
9993
9994       old_bit_depth=ping_bit_depth;
9995
9996       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9997         {
9998           if (image->alpha_trait == UndefinedPixelTrait &&
9999                ping_have_non_bw == MagickFalse)
10000              ping_bit_depth=1;
10001         }
10002
10003       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
10004         {
10005            size_t one = 1;
10006            ping_bit_depth=1;
10007
10008            if (image->colors == 0)
10009            {
10010               /* DO SOMETHING */
10011                 png_error(ping,"image has 0 colors");
10012            }
10013
10014            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
10015              ping_bit_depth <<= 1;
10016         }
10017
10018       if (logging != MagickFalse)
10019          {
10020            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10021             "    Number of colors: %.20g",(double) image_colors);
10022
10023            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10024             "    Tentative PNG bit depth: %d",ping_bit_depth);
10025          }
10026
10027       if (ping_bit_depth < (int) mng_info->write_png_depth)
10028          ping_bit_depth = mng_info->write_png_depth;
10029     }
10030
10031   image_depth=ping_bit_depth;
10032
10033   if (logging != MagickFalse)
10034     {
10035       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10036         "    Tentative PNG color type: %s (%.20g)",
10037         PngColorTypeToString(ping_color_type),
10038         (double) ping_color_type);
10039
10040       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10041         "    image_info->type: %.20g",(double) image_info->type);
10042
10043       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10044         "    image_depth: %.20g",(double) image_depth);
10045
10046       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10047
10048         "    image->depth: %.20g",(double) image->depth);
10049
10050       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10051         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
10052     }
10053
10054   if (matte != MagickFalse)
10055     {
10056       if (mng_info->IsPalette)
10057         {
10058           if (mng_info->write_png_colortype == 0)
10059             {
10060               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10061
10062               if (ping_have_color != MagickFalse)
10063                  ping_color_type=PNG_COLOR_TYPE_RGBA;
10064             }
10065
10066           /*
10067            * Determine if there is any transparent color.
10068           */
10069           if (number_transparent + number_semitransparent == 0)
10070             {
10071               /*
10072                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
10073               */
10074
10075               image_matte=MagickFalse;
10076
10077               if (mng_info->write_png_colortype == 0)
10078                 ping_color_type&=0x03;
10079             }
10080
10081           else
10082             {
10083               unsigned int
10084                 mask;
10085
10086               mask=0xffff;
10087
10088               if (ping_bit_depth == 8)
10089                  mask=0x00ff;
10090
10091               if (ping_bit_depth == 4)
10092                  mask=0x000f;
10093
10094               if (ping_bit_depth == 2)
10095                  mask=0x0003;
10096
10097               if (ping_bit_depth == 1)
10098                  mask=0x0001;
10099
10100               ping_trans_color.red=(png_uint_16)
10101                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
10102
10103               ping_trans_color.green=(png_uint_16)
10104                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
10105
10106               ping_trans_color.blue=(png_uint_16)
10107                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
10108
10109               ping_trans_color.gray=(png_uint_16)
10110                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
10111                    image->colormap)) & mask);
10112
10113               ping_trans_color.index=(png_byte) 0;
10114
10115               ping_have_tRNS=MagickTrue;
10116             }
10117
10118           if (ping_have_tRNS != MagickFalse)
10119             {
10120               /*
10121                * Determine if there is one and only one transparent color
10122                * and if so if it is fully transparent.
10123                */
10124               if (ping_have_cheap_transparency == MagickFalse)
10125                 ping_have_tRNS=MagickFalse;
10126             }
10127
10128           if (ping_have_tRNS != MagickFalse)
10129             {
10130               if (mng_info->write_png_colortype == 0)
10131                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
10132
10133               if (image_depth == 8)
10134                 {
10135                   ping_trans_color.red&=0xff;
10136                   ping_trans_color.green&=0xff;
10137                   ping_trans_color.blue&=0xff;
10138                   ping_trans_color.gray&=0xff;
10139                 }
10140             }
10141         }
10142       else
10143         {
10144           if (image_depth == 8)
10145             {
10146               ping_trans_color.red&=0xff;
10147               ping_trans_color.green&=0xff;
10148               ping_trans_color.blue&=0xff;
10149               ping_trans_color.gray&=0xff;
10150             }
10151         }
10152     }
10153
10154     matte=image_matte;
10155
10156     if (ping_have_tRNS != MagickFalse)
10157       image_matte=MagickFalse;
10158
10159     if ((mng_info->IsPalette) &&
10160         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
10161         ping_have_color == MagickFalse &&
10162         (image_matte == MagickFalse || image_depth >= 8))
10163       {
10164         size_t one=1;
10165
10166         if (image_matte != MagickFalse)
10167           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
10168
10169         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
10170           {
10171             ping_color_type=PNG_COLOR_TYPE_GRAY;
10172
10173             if (save_image_depth == 16 && image_depth == 8)
10174               {
10175                 if (logging != MagickFalse)
10176                   {
10177                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10178                         "  Scaling ping_trans_color (0)");
10179                   }
10180                     ping_trans_color.gray*=0x0101;
10181               }
10182           }
10183
10184         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
10185           image_depth=MAGICKCORE_QUANTUM_DEPTH;
10186
10187         if ((image_colors == 0) ||
10188              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
10189           image_colors=(int) (one << image_depth);
10190
10191         if (image_depth > 8)
10192           ping_bit_depth=16;
10193
10194         else
10195           {
10196             ping_bit_depth=8;
10197             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10198               {
10199                 if(!mng_info->write_png_depth)
10200                   {
10201                     ping_bit_depth=1;
10202
10203                     while ((int) (one << ping_bit_depth)
10204                         < (ssize_t) image_colors)
10205                       ping_bit_depth <<= 1;
10206                   }
10207               }
10208
10209             else if (ping_color_type ==
10210                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10211                 mng_info->IsPalette)
10212               {
10213               /* Check if grayscale is reducible */
10214
10215                 int
10216                   depth_4_ok=MagickTrue,
10217                   depth_2_ok=MagickTrue,
10218                   depth_1_ok=MagickTrue;
10219
10220                 for (i=0; i < (ssize_t) image_colors; i++)
10221                 {
10222                    unsigned char
10223                      intensity;
10224
10225                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10226
10227                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10228                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10229                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10230                      depth_2_ok=depth_1_ok=MagickFalse;
10231                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10232                      depth_1_ok=MagickFalse;
10233                 }
10234
10235                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10236                   ping_bit_depth=1;
10237
10238                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10239                   ping_bit_depth=2;
10240
10241                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10242                   ping_bit_depth=4;
10243               }
10244           }
10245
10246           image_depth=ping_bit_depth;
10247       }
10248
10249     else
10250
10251       if (mng_info->IsPalette)
10252       {
10253         number_colors=image_colors;
10254
10255         if (image_depth <= 8)
10256           {
10257             /*
10258               Set image palette.
10259             */
10260             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10261
10262             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10263               {
10264                 for (i=0; i < (ssize_t) number_colors; i++)
10265                 {
10266                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10267                   palette[i].green=
10268                     ScaleQuantumToChar(image->colormap[i].green);
10269                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10270                 }
10271
10272                 if (logging != MagickFalse)
10273                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10274                     "  Setting up PLTE chunk with %d colors",
10275                     number_colors);
10276
10277                 ping_have_PLTE=MagickTrue;
10278               }
10279
10280             /* color_type is PNG_COLOR_TYPE_PALETTE */
10281             if (mng_info->write_png_depth == 0)
10282               {
10283                 size_t
10284                   one;
10285
10286                 ping_bit_depth=1;
10287                 one=1;
10288
10289                 while ((one << ping_bit_depth) < (size_t) number_colors)
10290                   ping_bit_depth <<= 1;
10291               }
10292
10293             ping_num_trans=0;
10294
10295             if (matte != MagickFalse)
10296               {
10297                 /*
10298                  * Set up trans_colors array.
10299                  */
10300                 assert(number_colors <= 256);
10301
10302                 ping_num_trans=(unsigned short) (number_transparent +
10303                   number_semitransparent);
10304
10305                 if (ping_num_trans == 0)
10306                   ping_have_tRNS=MagickFalse;
10307
10308                 else
10309                   {
10310                     if (logging != MagickFalse)
10311                       {
10312                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10313                           "  Scaling ping_trans_color (1)");
10314                       }
10315                     ping_have_tRNS=MagickTrue;
10316
10317                     for (i=0; i < ping_num_trans; i++)
10318                     {
10319                        ping_trans_alpha[i]= (png_byte)
10320                          ScaleQuantumToChar(image->colormap[i].alpha);
10321                     }
10322                   }
10323               }
10324           }
10325       }
10326
10327     else
10328       {
10329
10330         if (image_depth < 8)
10331           image_depth=8;
10332
10333         if ((save_image_depth == 16) && (image_depth == 8))
10334           {
10335             if (logging != MagickFalse)
10336               {
10337                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10338                   "    Scaling ping_trans_color from (%d,%d,%d)",
10339                   (int) ping_trans_color.red,
10340                   (int) ping_trans_color.green,
10341                   (int) ping_trans_color.blue);
10342               }
10343
10344             ping_trans_color.red*=0x0101;
10345             ping_trans_color.green*=0x0101;
10346             ping_trans_color.blue*=0x0101;
10347             ping_trans_color.gray*=0x0101;
10348
10349             if (logging != MagickFalse)
10350               {
10351                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10352                   "    to (%d,%d,%d)",
10353                   (int) ping_trans_color.red,
10354                   (int) ping_trans_color.green,
10355                   (int) ping_trans_color.blue);
10356               }
10357           }
10358       }
10359
10360     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10361          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10362
10363     /*
10364       Adjust background and transparency samples in sub-8-bit grayscale files.
10365     */
10366     if (ping_bit_depth < 8 && ping_color_type ==
10367         PNG_COLOR_TYPE_GRAY)
10368       {
10369          png_uint_16
10370            maxval;
10371
10372          size_t
10373            one=1;
10374
10375          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10376
10377          if (ping_exclude_bKGD == MagickFalse)
10378          {
10379
10380          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10381            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10382            &image->background_color))) +.5)));
10383
10384          if (logging != MagickFalse)
10385            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10386              "  Setting up bKGD chunk (2)");
10387          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10388              "      background_color index is %d",
10389              (int) ping_background.index);
10390
10391          ping_have_bKGD = MagickTrue;
10392          }
10393
10394          if (logging != MagickFalse)
10395            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10396              "  Scaling ping_trans_color.gray from %d",
10397              (int)ping_trans_color.gray);
10398
10399          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10400            ping_trans_color.gray)+.5);
10401
10402          if (logging != MagickFalse)
10403            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10404              "      to %d", (int)ping_trans_color.gray);
10405       }
10406
10407   if (ping_exclude_bKGD == MagickFalse)
10408   {
10409     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10410       {
10411         /*
10412            Identify which colormap entry is the background color.
10413         */
10414
10415         number_colors=image_colors;
10416
10417         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10418           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10419             break;
10420
10421         ping_background.index=(png_byte) i;
10422
10423         if (logging != MagickFalse)
10424           {
10425             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10426               "  Setting up bKGD chunk with index=%d",(int) i);
10427           }
10428
10429         if (i < (ssize_t) number_colors)
10430           {
10431             ping_have_bKGD = MagickTrue;
10432
10433             if (logging != MagickFalse)
10434               {
10435                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10436                   "     background   =(%d,%d,%d)",
10437                         (int) ping_background.red,
10438                         (int) ping_background.green,
10439                         (int) ping_background.blue);
10440               }
10441           }
10442
10443         else  /* Can't happen */
10444           {
10445             if (logging != MagickFalse)
10446               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10447                   "      No room in PLTE to add bKGD color");
10448             ping_have_bKGD = MagickFalse;
10449           }
10450       }
10451   }
10452
10453   if (logging != MagickFalse)
10454     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10455       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10456       ping_color_type);
10457   /*
10458     Initialize compression level and filtering.
10459   */
10460   if (logging != MagickFalse)
10461     {
10462       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10463         "  Setting up deflate compression");
10464
10465       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10466         "    Compression buffer size: 32768");
10467     }
10468
10469   png_set_compression_buffer_size(ping,32768L);
10470
10471   if (logging != MagickFalse)
10472     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10473       "    Compression mem level: 9");
10474
10475   png_set_compression_mem_level(ping, 9);
10476
10477   /* Untangle the "-quality" setting:
10478
10479      Undefined is 0; the default is used.
10480      Default is 75
10481
10482      10's digit:
10483
10484         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10485            zlib default compression level
10486
10487         1-9: the zlib compression level
10488
10489      1's digit:
10490
10491         0-4: the PNG filter method
10492
10493         5:   libpng adaptive filtering if compression level > 5
10494              libpng filter type "none" if compression level <= 5
10495                 or if image is grayscale or palette
10496
10497         6:   libpng adaptive filtering
10498
10499         7:   "LOCO" filtering (intrapixel differing) if writing
10500              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10501              and earlier because of a missing "else".
10502
10503         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10504              filtering. Unused prior to IM-6.7.0-10, was same as 6
10505
10506         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10507              Unused prior to IM-6.7.0-10, was same as 6
10508
10509     Note that using the -quality option, not all combinations of
10510     PNG filter type, zlib compression level, and zlib compression
10511     strategy are possible.  This will be addressed soon in a
10512     release that accomodates "-define png:compression-strategy", etc.
10513
10514    */
10515
10516   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10517      image_info->quality;
10518
10519   if (quality <= 9)
10520     {
10521       if (mng_info->write_png_compression_strategy == 0)
10522         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10523     }
10524
10525   else if (mng_info->write_png_compression_level == 0)
10526     {
10527       int
10528         level;
10529
10530       level=(int) MagickMin((ssize_t) quality/10,9);
10531
10532       mng_info->write_png_compression_level = level+1;
10533     }
10534
10535   if (mng_info->write_png_compression_strategy == 0)
10536     {
10537         if ((quality %10) == 8 || (quality %10) == 9)
10538 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10539           mng_info->write_png_compression_strategy=Z_RLE+1;
10540 #else
10541           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10542 #endif
10543     }
10544
10545   if (mng_info->write_png_compression_filter == 0)
10546         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10547
10548   if (logging != MagickFalse)
10549     {
10550         if (mng_info->write_png_compression_level)
10551           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10552             "    Compression level:    %d",
10553             (int) mng_info->write_png_compression_level-1);
10554
10555         if (mng_info->write_png_compression_strategy)
10556           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10557             "    Compression strategy: %d",
10558             (int) mng_info->write_png_compression_strategy-1);
10559
10560         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10561           "  Setting up filtering");
10562
10563         if (mng_info->write_png_compression_filter == 6)
10564           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10565             "    Base filter method: ADAPTIVE");
10566         else if (mng_info->write_png_compression_filter == 0 ||
10567                  mng_info->write_png_compression_filter == 1)
10568           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569             "    Base filter method: NONE");
10570         else
10571           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10572             "    Base filter method: %d",
10573             (int) mng_info->write_png_compression_filter-1);
10574     }
10575
10576   if (mng_info->write_png_compression_level != 0)
10577     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10578
10579   if (mng_info->write_png_compression_filter == 6)
10580     {
10581       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10582          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10583          (quality < 50))
10584         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10585       else
10586         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10587      }
10588   else if (mng_info->write_png_compression_filter == 7 ||
10589       mng_info->write_png_compression_filter == 10)
10590     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10591
10592   else if (mng_info->write_png_compression_filter == 8)
10593     {
10594 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10595       if (mng_info->write_mng)
10596       {
10597          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10598              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10599         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10600       }
10601 #endif
10602       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10603     }
10604
10605   else if (mng_info->write_png_compression_filter == 9)
10606     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10607
10608   else if (mng_info->write_png_compression_filter != 0)
10609     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10610        mng_info->write_png_compression_filter-1);
10611
10612   if (mng_info->write_png_compression_strategy != 0)
10613     png_set_compression_strategy(ping,
10614        mng_info->write_png_compression_strategy-1);
10615
10616   ping_interlace_method=image_info->interlace != NoInterlace;
10617
10618   if (mng_info->write_mng)
10619     png_set_sig_bytes(ping,8);
10620
10621   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10622
10623   if (mng_info->write_png_colortype != 0)
10624     {
10625      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10626        if (ping_have_color != MagickFalse)
10627          {
10628            ping_color_type = PNG_COLOR_TYPE_RGB;
10629
10630            if (ping_bit_depth < 8)
10631              ping_bit_depth=8;
10632          }
10633
10634      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10635        if (ping_have_color != MagickFalse)
10636          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10637     }
10638
10639   if (ping_need_colortype_warning != MagickFalse ||
10640      ((mng_info->write_png_depth &&
10641      (int) mng_info->write_png_depth != ping_bit_depth) ||
10642      (mng_info->write_png_colortype &&
10643      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10644       mng_info->write_png_colortype != 7 &&
10645       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10646     {
10647       if (logging != MagickFalse)
10648         {
10649           if (ping_need_colortype_warning != MagickFalse)
10650             {
10651               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10652                  "  Image has transparency but tRNS chunk was excluded");
10653             }
10654
10655           if (mng_info->write_png_depth)
10656             {
10657               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10658                   "  Defined png:bit-depth=%u, Computed depth=%u",
10659                   mng_info->write_png_depth,
10660                   ping_bit_depth);
10661             }
10662
10663           if (mng_info->write_png_colortype)
10664             {
10665               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10666                   "  Defined png:color-type=%u, Computed color type=%u",
10667                   mng_info->write_png_colortype-1,
10668                   ping_color_type);
10669             }
10670         }
10671
10672       png_warning(ping,
10673         "Cannot write image with defined png:bit-depth or png:color-type.");
10674     }
10675
10676   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10677     {
10678       /* Add an opaque matte channel */
10679       image->alpha_trait = BlendPixelTrait;
10680       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10681
10682       if (logging != MagickFalse)
10683         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10684           "  Added an opaque matte channel");
10685     }
10686
10687   if (number_transparent != 0 || number_semitransparent != 0)
10688     {
10689       if (ping_color_type < 4)
10690         {
10691            ping_have_tRNS=MagickTrue;
10692            if (logging != MagickFalse)
10693              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10694                "  Setting ping_have_tRNS=MagickTrue.");
10695         }
10696     }
10697
10698   if (logging != MagickFalse)
10699     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10700       "  Writing PNG header chunks");
10701
10702   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10703                ping_bit_depth,ping_color_type,
10704                ping_interlace_method,ping_compression_method,
10705                ping_filter_method);
10706
10707   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10708     {
10709       png_set_PLTE(ping,ping_info,palette,number_colors);
10710
10711       if (logging != MagickFalse)
10712         {
10713           for (i=0; i< (ssize_t) number_colors; i++)
10714           {
10715             if (i < ping_num_trans)
10716               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10717                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10718                       (int) i,
10719                       (int) palette[i].red,
10720                       (int) palette[i].green,
10721                       (int) palette[i].blue,
10722                       (int) i,
10723                       (int) ping_trans_alpha[i]);
10724              else
10725               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10726                 "     PLTE[%d] = (%d,%d,%d)",
10727                       (int) i,
10728                       (int) palette[i].red,
10729                       (int) palette[i].green,
10730                       (int) palette[i].blue);
10731            }
10732          }
10733     }
10734
10735   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10736   if (ping_exclude_sRGB != MagickFalse ||
10737      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10738   {
10739     if ((ping_exclude_tEXt == MagickFalse ||
10740        ping_exclude_zTXt == MagickFalse) &&
10741        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10742     {
10743       ResetImageProfileIterator(image);
10744       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10745       {
10746         profile=GetImageProfile(image,name);
10747
10748         if (profile != (StringInfo *) NULL)
10749           {
10750 #ifdef PNG_WRITE_iCCP_SUPPORTED
10751             if ((LocaleCompare(name,"ICC") == 0) ||
10752                 (LocaleCompare(name,"ICM") == 0))
10753               {
10754                 ping_have_iCCP = MagickTrue;
10755                 if (ping_exclude_iCCP == MagickFalse)
10756                   {
10757                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10758                         "  Setting up iCCP chunk");
10759
10760                     png_set_iCCP(ping,ping_info,(png_charp) name,0,
10761 #if (PNG_LIBPNG_VER < 10500)
10762                     (png_charp) GetStringInfoDatum(profile),
10763 #else
10764                     (const png_byte *) GetStringInfoDatum(profile),
10765 #endif
10766                     (png_uint_32) GetStringInfoLength(profile));
10767                   }
10768                 else
10769                   {
10770                     /* Do not write hex-encoded ICC chunk */
10771                        name=GetNextImageProfile(image);
10772                        continue;
10773                   }
10774               }
10775 #endif /* WRITE_iCCP */
10776
10777             if (LocaleCompare(name,"exif") == 0)
10778               {
10779                    /* Do not write hex-encoded ICC chunk; we will
10780                       write it later as an eXIf chunk */
10781                    name=GetNextImageProfile(image);
10782                    continue;
10783               }
10784
10785               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10786                  "  Setting up zTXt chunk with uuencoded %s profile",
10787                  name);
10788               Magick_png_write_raw_profile(image_info,ping,ping_info,
10789                 (unsigned char *) name,(unsigned char *) name,
10790                 GetStringInfoDatum(profile),
10791                 (png_uint_32) GetStringInfoLength(profile));
10792           }
10793         name=GetNextImageProfile(image);
10794       }
10795     }
10796   }
10797
10798 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10799   if ((mng_info->have_write_global_srgb == 0) &&
10800       ping_have_iCCP != MagickTrue &&
10801       (ping_have_sRGB != MagickFalse ||
10802       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10803     {
10804       if (ping_exclude_sRGB == MagickFalse)
10805         {
10806           /*
10807             Note image rendering intent.
10808           */
10809           if (logging != MagickFalse)
10810             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10811                 "  Setting up sRGB chunk");
10812
10813           (void) png_set_sRGB(ping,ping_info,(
10814             Magick_RenderingIntent_to_PNG_RenderingIntent(
10815               image->rendering_intent)));
10816
10817           ping_have_sRGB = MagickTrue;
10818         }
10819     }
10820
10821   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10822 #endif
10823     {
10824       if (ping_exclude_gAMA == MagickFalse &&
10825           ping_have_iCCP == MagickFalse &&
10826           ping_have_sRGB == MagickFalse &&
10827           (ping_exclude_sRGB == MagickFalse ||
10828           (image->gamma < .45 || image->gamma > .46)))
10829       {
10830       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10831         {
10832           /*
10833             Note image gamma.
10834             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10835           */
10836           if (logging != MagickFalse)
10837             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10838               "  Setting up gAMA chunk");
10839
10840           png_set_gAMA(ping,ping_info,image->gamma);
10841         }
10842       }
10843
10844       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10845         {
10846           if ((mng_info->have_write_global_chrm == 0) &&
10847               (image->chromaticity.red_primary.x != 0.0))
10848             {
10849               /*
10850                 Note image chromaticity.
10851                 Note: if cHRM+gAMA == sRGB write sRGB instead.
10852               */
10853                PrimaryInfo
10854                  bp,
10855                  gp,
10856                  rp,
10857                  wp;
10858
10859                wp=image->chromaticity.white_point;
10860                rp=image->chromaticity.red_primary;
10861                gp=image->chromaticity.green_primary;
10862                bp=image->chromaticity.blue_primary;
10863
10864                if (logging != MagickFalse)
10865                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10866                    "  Setting up cHRM chunk");
10867
10868                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10869                    bp.x,bp.y);
10870            }
10871         }
10872     }
10873
10874   if (ping_exclude_bKGD == MagickFalse)
10875     {
10876       if (ping_have_bKGD != MagickFalse)
10877         {
10878           png_set_bKGD(ping,ping_info,&ping_background);
10879           if (logging != MagickFalse)
10880             {
10881               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10882                    "    Setting up bKGD chunk");
10883               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10884                    "      background color = (%d,%d,%d)",
10885                         (int) ping_background.red,
10886                         (int) ping_background.green,
10887                         (int) ping_background.blue);
10888               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10889                    "      index = %d, gray=%d",
10890                         (int) ping_background.index,
10891                         (int) ping_background.gray);
10892             }
10893          }
10894     }
10895
10896   if (ping_exclude_pHYs == MagickFalse)
10897     {
10898       if (ping_have_pHYs != MagickFalse)
10899         {
10900           png_set_pHYs(ping,ping_info,
10901              ping_pHYs_x_resolution,
10902              ping_pHYs_y_resolution,
10903              ping_pHYs_unit_type);
10904
10905           if (logging != MagickFalse)
10906             {
10907               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10908                    "    Setting up pHYs chunk");
10909               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10910                    "      x_resolution=%lu",
10911                    (unsigned long) ping_pHYs_x_resolution);
10912               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10913                    "      y_resolution=%lu",
10914                    (unsigned long) ping_pHYs_y_resolution);
10915               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10916                    "      unit_type=%lu",
10917                    (unsigned long) ping_pHYs_unit_type);
10918             }
10919         }
10920     }
10921
10922 #if defined(PNG_tIME_SUPPORTED)
10923   if (ping_exclude_tIME == MagickFalse)
10924     {
10925       const char
10926         *timestamp;
10927
10928       if (image->taint == MagickFalse)
10929         {
10930           timestamp=GetImageOption(image_info,"png:tIME");
10931
10932           if (timestamp == (const char *) NULL)
10933             timestamp=GetImageProperty(image,"png:tIME",exception);
10934         }
10935
10936       else
10937         {
10938           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10939              "  Reset tIME in tainted image");
10940
10941           timestamp=GetImageProperty(image,"date:modify",exception);
10942         }
10943
10944       if (timestamp != (const char *) NULL)
10945           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10946     }
10947 #endif
10948
10949   if (mng_info->need_blob != MagickFalse)
10950   {
10951     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10952        MagickFalse)
10953        png_error(ping,"WriteBlob Failed");
10954
10955      ping_have_blob=MagickTrue;
10956   }
10957
10958   png_write_info_before_PLTE(ping, ping_info);
10959
10960   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10961     {
10962       if (logging != MagickFalse)
10963         {
10964           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10965               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10966         }
10967
10968       if (ping_color_type == 3)
10969          (void) png_set_tRNS(ping, ping_info,
10970                 ping_trans_alpha,
10971                 ping_num_trans,
10972                 NULL);
10973
10974       else
10975         {
10976            (void) png_set_tRNS(ping, ping_info,
10977                   NULL,
10978                   0,
10979                   &ping_trans_color);
10980
10981            if (logging != MagickFalse)
10982              {
10983                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10984                  "     tRNS color   =(%d,%d,%d)",
10985                        (int) ping_trans_color.red,
10986                        (int) ping_trans_color.green,
10987                        (int) ping_trans_color.blue);
10988              }
10989          }
10990     }
10991
10992   png_write_info(ping,ping_info);
10993
10994   ping_wrote_caNv = MagickFalse;
10995
10996   /* write caNv chunk */
10997   if (ping_exclude_caNv == MagickFalse)
10998     {
10999       if ((image->page.width != 0 && image->page.width != image->columns) ||
11000           (image->page.height != 0 && image->page.height != image->rows) ||
11001           image->page.x != 0 || image->page.y != 0)
11002         {
11003           unsigned char
11004             chunk[20];
11005
11006           (void) WriteBlobMSBULong(image,16L);  /* data length=8 */
11007           PNGType(chunk,mng_caNv);
11008           LogPNGChunk(logging,mng_caNv,16L);
11009           PNGLong(chunk+4,(png_uint_32) image->page.width);
11010           PNGLong(chunk+8,(png_uint_32) image->page.height);
11011           PNGsLong(chunk+12,(png_int_32) image->page.x);
11012           PNGsLong(chunk+16,(png_int_32) image->page.y);
11013           (void) WriteBlob(image,20,chunk);
11014           (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
11015           ping_wrote_caNv = MagickTrue;
11016         }
11017     }
11018
11019 #if defined(PNG_oFFs_SUPPORTED)
11020   if (ping_exclude_oFFs == MagickFalse && ping_wrote_caNv == MagickFalse)
11021     {
11022       if (image->page.x || image->page.y)
11023         {
11024            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
11025               (png_int_32) image->page.y, 0);
11026
11027            if (logging != MagickFalse)
11028              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11029                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
11030                  (int) image->page.x, (int) image->page.y);
11031         }
11032     }
11033 #endif
11034
11035   /* write vpAg chunk (deprecated, replaced by caNv) */
11036   if (ping_exclude_vpAg == MagickFalse && ping_wrote_caNv == MagickFalse)
11037     {
11038       if ((image->page.width != 0 && image->page.width != image->columns) ||
11039           (image->page.height != 0 && image->page.height != image->rows))
11040         {
11041           unsigned char
11042             chunk[14];
11043
11044           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
11045           PNGType(chunk,mng_vpAg);
11046           LogPNGChunk(logging,mng_vpAg,9L);
11047           PNGLong(chunk+4,(png_uint_32) image->page.width);
11048           PNGLong(chunk+8,(png_uint_32) image->page.height);
11049           chunk[12]=0;   /* unit = pixels */
11050           (void) WriteBlob(image,13,chunk);
11051           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
11052         }
11053     }
11054
11055 #if (PNG_LIBPNG_VER == 10206)
11056     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
11057 #define PNG_HAVE_IDAT               0x04
11058     ping->mode |= PNG_HAVE_IDAT;
11059 #undef PNG_HAVE_IDAT
11060 #endif
11061
11062   png_set_packing(ping);
11063   /*
11064     Allocate memory.
11065   */
11066   rowbytes=image->columns;
11067   if (image_depth > 8)
11068     rowbytes*=2;
11069   switch (ping_color_type)
11070     {
11071       case PNG_COLOR_TYPE_RGB:
11072         rowbytes*=3;
11073         break;
11074
11075       case PNG_COLOR_TYPE_GRAY_ALPHA:
11076         rowbytes*=2;
11077         break;
11078
11079       case PNG_COLOR_TYPE_RGBA:
11080         rowbytes*=4;
11081         break;
11082
11083       default:
11084         break;
11085     }
11086
11087   if (logging != MagickFalse)
11088     {
11089       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11090         "  Writing PNG image data");
11091
11092       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11093         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
11094     }
11095   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
11096   if (pixel_info == (MemoryInfo *) NULL)
11097     png_error(ping,"Allocation of memory for pixels failed");
11098   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
11099
11100   /*
11101     Initialize image scanlines.
11102   */
11103   quantum_info=AcquireQuantumInfo(image_info,image);
11104   if (quantum_info == (QuantumInfo *) NULL)
11105     png_error(ping,"Memory allocation for quantum_info failed");
11106   quantum_info->format=UndefinedQuantumFormat;
11107   SetQuantumDepth(image,quantum_info,image_depth);
11108   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
11109   num_passes=png_set_interlace_handling(ping);
11110
11111   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11112        !mng_info->write_png48 && !mng_info->write_png64 &&
11113        !mng_info->write_png32) &&
11114        (mng_info->IsPalette ||
11115        (image_info->type == BilevelType)) &&
11116        image_matte == MagickFalse &&
11117        ping_have_non_bw == MagickFalse)
11118     {
11119       /* Palette, Bilevel, or Opaque Monochrome */
11120       register const Quantum
11121         *p;
11122
11123       SetQuantumDepth(image,quantum_info,8);
11124       for (pass=0; pass < num_passes; pass++)
11125       {
11126         /*
11127           Convert PseudoClass image to a PNG monochrome image.
11128         */
11129         for (y=0; y < (ssize_t) image->rows; y++)
11130         {
11131           if (logging != MagickFalse && y == 0)
11132              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11133                  "    Writing row of pixels (0)");
11134
11135           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11136
11137           if (p == (const Quantum *) NULL)
11138             break;
11139
11140           if (mng_info->IsPalette)
11141             {
11142               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11143                 quantum_info,GrayQuantum,ping_pixels,exception);
11144               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
11145                   mng_info->write_png_depth &&
11146                   mng_info->write_png_depth != old_bit_depth)
11147                 {
11148                   /* Undo pixel scaling */
11149                   for (i=0; i < (ssize_t) image->columns; i++)
11150                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
11151                      >> (8-old_bit_depth));
11152                 }
11153             }
11154
11155           else
11156             {
11157               (void) ExportQuantumPixels(image,(CacheView *) NULL,
11158                 quantum_info,RedQuantum,ping_pixels,exception);
11159             }
11160
11161           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
11162             for (i=0; i < (ssize_t) image->columns; i++)
11163                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
11164                       255 : 0);
11165
11166           if (logging != MagickFalse && y == 0)
11167             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11168                 "    Writing row of pixels (1)");
11169
11170           png_write_row(ping,ping_pixels);
11171
11172           status=SetImageProgress(image,SaveImageTag,
11173               (MagickOffsetType) (pass * image->rows + y),
11174               num_passes * image->rows);
11175
11176           if (status == MagickFalse)
11177             break;
11178         }
11179       }
11180     }
11181
11182   else   /* Not Palette, Bilevel, or Opaque Monochrome */
11183     {
11184       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
11185           !mng_info->write_png48 && !mng_info->write_png64 &&
11186           !mng_info->write_png32) && (image_matte != MagickFalse ||
11187           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
11188           (mng_info->IsPalette) && ping_have_color == MagickFalse)
11189         {
11190           register const Quantum
11191             *p;
11192
11193           for (pass=0; pass < num_passes; pass++)
11194           {
11195
11196           for (y=0; y < (ssize_t) image->rows; y++)
11197           {
11198             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
11199
11200             if (p == (const Quantum *) NULL)
11201               break;
11202
11203             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11204               {
11205                 if (mng_info->IsPalette)
11206                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11207                     quantum_info,GrayQuantum,ping_pixels,exception);
11208
11209                 else
11210                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11211                     quantum_info,RedQuantum,ping_pixels,exception);
11212
11213                 if (logging != MagickFalse && y == 0)
11214                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11215                        "    Writing GRAY PNG pixels (2)");
11216               }
11217
11218             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
11219               {
11220                 if (logging != MagickFalse && y == 0)
11221                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11222                          "    Writing GRAY_ALPHA PNG pixels (2)");
11223
11224                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
11225                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
11226               }
11227
11228             if (logging != MagickFalse && y == 0)
11229               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11230                   "    Writing row of pixels (2)");
11231
11232             png_write_row(ping,ping_pixels);
11233
11234             status=SetImageProgress(image,SaveImageTag,
11235               (MagickOffsetType) (pass * image->rows + y),
11236               num_passes * image->rows);
11237
11238             if (status == MagickFalse)
11239               break;
11240             }
11241           }
11242         }
11243
11244       else
11245         {
11246           register const Quantum
11247             *p;
11248
11249           for (pass=0; pass < num_passes; pass++)
11250           {
11251             if ((image_depth > 8) ||
11252                 mng_info->write_png24 ||
11253                 mng_info->write_png32 ||
11254                 mng_info->write_png48 ||
11255                 mng_info->write_png64 ||
11256                 (!mng_info->write_png8 && !mng_info->IsPalette))
11257             {
11258               for (y=0; y < (ssize_t) image->rows; y++)
11259               {
11260                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11261
11262                 if (p == (const Quantum *) NULL)
11263                   break;
11264
11265                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11266                   {
11267                     if (image->storage_class == DirectClass)
11268                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11269                         quantum_info,RedQuantum,ping_pixels,exception);
11270
11271                     else
11272                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11273                         quantum_info,GrayQuantum,ping_pixels,exception);
11274                   }
11275
11276                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11277                   {
11278                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11279                       quantum_info,GrayAlphaQuantum,ping_pixels,
11280                       exception);
11281
11282                     if (logging != MagickFalse && y == 0)
11283                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11284                            "    Writing GRAY_ALPHA PNG pixels (3)");
11285                   }
11286
11287                 else if (image_matte != MagickFalse)
11288                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11289                     quantum_info,RGBAQuantum,ping_pixels,exception);
11290
11291                 else
11292                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11293                     quantum_info,RGBQuantum,ping_pixels,exception);
11294
11295                 if (logging != MagickFalse && y == 0)
11296                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11297                       "    Writing row of pixels (3)");
11298
11299                 png_write_row(ping,ping_pixels);
11300
11301                 status=SetImageProgress(image,SaveImageTag,
11302                   (MagickOffsetType) (pass * image->rows + y),
11303                   num_passes * image->rows);
11304
11305                 if (status == MagickFalse)
11306                   break;
11307               }
11308             }
11309
11310           else
11311             /* not ((image_depth > 8) ||
11312                 mng_info->write_png24 || mng_info->write_png32 ||
11313                 mng_info->write_png48 || mng_info->write_png64 ||
11314                 (!mng_info->write_png8 && !mng_info->IsPalette))
11315              */
11316             {
11317               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11318                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11319                 {
11320                   if (logging != MagickFalse)
11321                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11322                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11323
11324                   SetQuantumDepth(image,quantum_info,8);
11325                   image_depth=8;
11326                 }
11327
11328               for (y=0; y < (ssize_t) image->rows; y++)
11329               {
11330                 if (logging != MagickFalse && y == 0)
11331                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11332                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",
11333                     pass);
11334
11335                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11336
11337                 if (p == (const Quantum *) NULL)
11338                   break;
11339
11340                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11341                   {
11342                     SetQuantumDepth(image,quantum_info,image->depth);
11343
11344                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11345                        quantum_info,GrayQuantum,ping_pixels,exception);
11346                   }
11347
11348                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11349                   {
11350                     if (logging != MagickFalse && y == 0)
11351                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11352                            "  Writing GRAY_ALPHA PNG pixels (4)");
11353
11354                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11355                          quantum_info,GrayAlphaQuantum,ping_pixels,
11356                          exception);
11357                   }
11358
11359                 else
11360                   {
11361                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11362                       quantum_info,IndexQuantum,ping_pixels,exception);
11363
11364                     if (logging != MagickFalse && y <= 2)
11365                     {
11366                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11367                           "  Writing row of non-gray pixels (4)");
11368
11369                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11370                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11371                           (int)ping_pixels[0],(int)ping_pixels[1]);
11372                     }
11373                   }
11374                 png_write_row(ping,ping_pixels);
11375
11376                 status=SetImageProgress(image,SaveImageTag,
11377                   (MagickOffsetType) (pass * image->rows + y),
11378                   num_passes * image->rows);
11379
11380                 if (status == MagickFalse)
11381                   break;
11382               }
11383             }
11384           }
11385         }
11386     }
11387
11388   if (quantum_info != (QuantumInfo *) NULL)
11389     quantum_info=DestroyQuantumInfo(quantum_info);
11390
11391   if (logging != MagickFalse)
11392     {
11393       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11394         "  Wrote PNG image data");
11395
11396       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11397         "    Width: %.20g",(double) ping_width);
11398
11399       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11400         "    Height: %.20g",(double) ping_height);
11401
11402       if (mng_info->write_png_depth)
11403         {
11404           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11405             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11406         }
11407
11408       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11409         "    PNG bit-depth written: %d",ping_bit_depth);
11410
11411       if (mng_info->write_png_colortype)
11412         {
11413           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11414             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11415         }
11416
11417       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11418         "    PNG color-type written: %d",ping_color_type);
11419
11420       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11421         "    PNG Interlace method: %d",ping_interlace_method);
11422     }
11423   /*
11424     Generate text chunks after IDAT.
11425   */
11426   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11427   {
11428     ResetImagePropertyIterator(image);
11429     property=GetNextImageProperty(image);
11430     while (property != (const char *) NULL)
11431     {
11432       png_textp
11433         text;
11434
11435       value=GetImageProperty(image,property,exception);
11436
11437       /* Don't write any "png:" or "jpeg:" properties; those are just for
11438        * "identify" or for passing through to another JPEG
11439        */
11440       if ((LocaleNCompare(property,"png:",4) != 0 &&
11441            LocaleNCompare(property,"jpeg:",5) != 0) &&
11442
11443
11444           /* Suppress density and units if we wrote a pHYs chunk */
11445           (ping_exclude_pHYs != MagickFalse      ||
11446           LocaleCompare(property,"density") != 0 ||
11447           LocaleCompare(property,"units") != 0) &&
11448
11449           /* Suppress the IM-generated Date:create and Date:modify */
11450           (ping_exclude_date == MagickFalse      ||
11451           LocaleNCompare(property, "Date:",5) != 0))
11452         {
11453         if (value != (const char *) NULL)
11454           {
11455
11456 #if PNG_LIBPNG_VER >= 10400
11457             text=(png_textp) png_malloc(ping,
11458                  (png_alloc_size_t) sizeof(png_text));
11459 #else
11460             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11461 #endif
11462             text[0].key=(char *) property;
11463             text[0].text=(char *) value;
11464             text[0].text_length=strlen(value);
11465
11466             if (ping_exclude_tEXt != MagickFalse)
11467                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11468
11469             else if (ping_exclude_zTXt != MagickFalse)
11470                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11471
11472             else
11473             {
11474                text[0].compression=image_info->compression == NoCompression ||
11475                  (image_info->compression == UndefinedCompression &&
11476                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11477                  PNG_TEXT_COMPRESSION_zTXt ;
11478             }
11479
11480             if (logging != MagickFalse)
11481               {
11482                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11483                   "  Setting up text chunk");
11484
11485                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11486                   "    keyword: '%s'",text[0].key);
11487               }
11488
11489             png_set_text(ping,ping_info,text,1);
11490             png_free(ping,text);
11491           }
11492         }
11493       property=GetNextImageProperty(image);
11494     }
11495   }
11496
11497   /* write eXIf profile */
11498   if (ping_have_eXIf != MagickFalse && ping_exclude_eXIf == MagickFalse)
11499     {
11500       char
11501         *name;
11502
11503       ResetImageProfileIterator(image);
11504
11505       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
11506       {
11507         if (LocaleCompare(name,"exif") == 0)
11508           {
11509             const StringInfo
11510               *profile;
11511
11512             profile=GetImageProfile(image,name);
11513
11514             if (profile != (StringInfo *) NULL)
11515               {
11516                 png_uint_32
11517                   length;
11518
11519                 unsigned char
11520                   chunk[4],
11521                   *data;
11522
11523                StringInfo
11524                  *ping_profile;
11525
11526                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11527                   "  Have eXIf profile");
11528
11529                ping_profile=CloneStringInfo(profile);
11530                data=GetStringInfoDatum(ping_profile),
11531                length=(png_uint_32) GetStringInfoLength(ping_profile);
11532
11533                PNGType(chunk,mng_eXIf);
11534                if (length < 7)
11535                  {
11536                    ping_profile=DestroyStringInfo(ping_profile);
11537                    break;  /* otherwise crashes */
11538                  }
11539
11540                /* skip the "Exif\0\0" JFIF Exif Header ID */
11541                length -= 6;
11542
11543                LogPNGChunk(logging,chunk,length);
11544                (void) WriteBlobMSBULong(image,length);
11545                (void) WriteBlob(image,4,chunk);
11546                (void) WriteBlob(image,length,data+6);
11547                (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),
11548                  data+6, (uInt) length));
11549                ping_profile=DestroyStringInfo(ping_profile);
11550                break;
11551              }
11552          }
11553        name=GetNextImageProfile(image);
11554      }
11555   }
11556
11557   if (logging != MagickFalse)
11558     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11559       "  Writing PNG end info");
11560
11561   png_write_end(ping,ping_info);
11562
11563   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11564     {
11565       if (mng_info->page.x || mng_info->page.y ||
11566           (ping_width != mng_info->page.width) ||
11567           (ping_height != mng_info->page.height))
11568         {
11569           unsigned char
11570             chunk[32];
11571
11572           /*
11573             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11574           */
11575           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11576           PNGType(chunk,mng_FRAM);
11577           LogPNGChunk(logging,mng_FRAM,27L);
11578           chunk[4]=4;
11579           chunk[5]=0;  /* frame name separator (no name) */
11580           chunk[6]=1;  /* flag for changing delay, for next frame only */
11581           chunk[7]=0;  /* flag for changing frame timeout */
11582           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11583           chunk[9]=0;  /* flag for changing frame sync_id */
11584           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11585           chunk[14]=0; /* clipping boundaries delta type */
11586           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11587           PNGLong(chunk+19,
11588              (png_uint_32) (mng_info->page.x + ping_width));
11589           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11590           PNGLong(chunk+27,
11591              (png_uint_32) (mng_info->page.y + ping_height));
11592           (void) WriteBlob(image,31,chunk);
11593           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11594           mng_info->old_framing_mode=4;
11595           mng_info->framing_mode=1;
11596         }
11597
11598       else
11599         mng_info->framing_mode=3;
11600     }
11601   if (mng_info->write_mng && !mng_info->need_fram &&
11602       ((int) image->dispose == 3))
11603      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11604
11605   /*
11606     Free PNG resources.
11607   */
11608
11609   png_destroy_write_struct(&ping,&ping_info);
11610
11611   pixel_info=RelinquishVirtualMemory(pixel_info);
11612
11613   if (ping_have_blob != MagickFalse)
11614      (void) CloseBlob(image);
11615
11616   image_info=DestroyImageInfo(image_info);
11617   image=DestroyImage(image);
11618
11619   /* Store bit depth actually written */
11620   s[0]=(char) ping_bit_depth;
11621   s[1]='\0';
11622
11623   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11624
11625   if (logging != MagickFalse)
11626     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11627       "  exit WriteOnePNGImage()");
11628
11629 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11630   UnlockSemaphoreInfo(ping_semaphore);
11631 #endif
11632
11633    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11634     *    Throwing an Exception when an error occurs.
11635     */
11636
11637   return(MagickTrue);
11638 /*  End write one PNG image */
11639
11640 }
11641
11642 /*
11643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11644 %                                                                             %
11645 %                                                                             %
11646 %                                                                             %
11647 %   W r i t e P N G I m a g e                                                 %
11648 %                                                                             %
11649 %                                                                             %
11650 %                                                                             %
11651 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11652 %
11653 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11654 %  Multiple-image Network Graphics (MNG) image file.
11655 %
11656 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11657 %
11658 %  The format of the WritePNGImage method is:
11659 %
11660 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11661 %        Image *image,ExceptionInfo *exception)
11662 %
11663 %  A description of each parameter follows:
11664 %
11665 %    o image_info: the image info.
11666 %
11667 %    o image:  The image.
11668 %
11669 %    o exception: return any errors or warnings in this structure.
11670 %
11671 %  Returns MagickTrue on success, MagickFalse on failure.
11672 %
11673 %  Communicating with the PNG encoder:
11674 %
11675 %  While the datastream written is always in PNG format and normally would
11676 %  be given the "png" file extension, this method also writes the following
11677 %  pseudo-formats which are subsets of png:
11678 %
11679 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11680 %               a depth greater than 8, the depth is reduced. If transparency
11681 %               is present, the tRNS chunk must only have values 0 and 255
11682 %               (i.e., transparency is binary: fully opaque or fully
11683 %               transparent).  If other values are present they will be
11684 %               50%-thresholded to binary transparency.  If more than 256
11685 %               colors are present, they will be quantized to the 4-4-4-1,
11686 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11687 %               of any resulting fully-transparent pixels is changed to
11688 %               the image's background color.
11689 %
11690 %               If you want better quantization or dithering of the colors
11691 %               or alpha than that, you need to do it before calling the
11692 %               PNG encoder. The pixels contain 8-bit indices even if
11693 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11694 %               images will be written as indexed PNG files even though the
11695 %               PNG grayscale type might be slightly more efficient.  Please
11696 %               note that writing to the PNG8 format may result in loss
11697 %               of color and alpha data.
11698 %
11699 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11700 %               chunk can be present to convey binary transparency by naming
11701 %               one of the colors as transparent.  The only loss incurred
11702 %               is reduction of sample depth to 8.  If the image has more
11703 %               than one transparent color, has semitransparent pixels, or
11704 %               has an opaque pixel with the same RGB components as the
11705 %               transparent color, an image is not written.
11706 %
11707 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11708 %               transparency is permitted, i.e., the alpha sample for
11709 %               each pixel can have any value from 0 to 255. The alpha
11710 %               channel is present even if the image is fully opaque.
11711 %               The only loss in data is the reduction of the sample depth
11712 %               to 8.
11713 %
11714 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11715 %               chunk can be present to convey binary transparency by naming
11716 %               one of the colors as transparent.  If the image has more
11717 %               than one transparent color, has semitransparent pixels, or
11718 %               has an opaque pixel with the same RGB components as the
11719 %               transparent color, an image is not written.
11720 %
11721 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11722 %               transparency is permitted, i.e., the alpha sample for
11723 %               each pixel can have any value from 0 to 65535. The alpha
11724 %               channel is present even if the image is fully opaque.
11725 %
11726 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11727 %               image, if the input was a PNG, is written.  If these values
11728 %               cannot be found, or if the pixels have been changed in a way
11729 %               that makes this impossible, then "PNG00" falls back to the
11730 %               regular "PNG" format.
11731 %
11732 %    o -define: For more precise control of the PNG output, you can use the
11733 %               Image options "png:bit-depth" and "png:color-type".  These
11734 %               can be set from the commandline with "-define" and also
11735 %               from the application programming interfaces.  The options
11736 %               are case-independent and are converted to lowercase before
11737 %               being passed to this encoder.
11738 %
11739 %               png:color-type can be 0, 2, 3, 4, or 6.
11740 %
11741 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11742 %               be 1, 2, 4, 8, or 16.
11743 %
11744 %               When png:color-type is 2 (RGB), png:bit-depth can
11745 %               be 8 or 16.
11746 %
11747 %               When png:color-type is 3 (Indexed), png:bit-depth can
11748 %               be 1, 2, 4, or 8.  This refers to the number of bits
11749 %               used to store the index.  The color samples always have
11750 %               bit-depth 8 in indexed PNG files.
11751 %
11752 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11753 %               png:bit-depth can be 8 or 16.
11754 %
11755 %               If the image cannot be written without loss with the
11756 %               requested bit-depth and color-type, a PNG file will not
11757 %               be written, a warning will be issued, and the encoder will
11758 %               return MagickFalse.
11759 %
11760 %  Since image encoders should not be responsible for the "heavy lifting",
11761 %  the user should make sure that ImageMagick has already reduced the
11762 %  image depth and number of colors and limit transparency to binary
11763 %  transparency prior to attempting to write the image with depth, color,
11764 %  or transparency limitations.
11765 %
11766 %  Note that another definition, "png:bit-depth-written" exists, but it
11767 %  is not intended for external use.  It is only used internally by the
11768 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11769 %
11770 %  As of version 6.6.6 the following optimizations are always done:
11771 %
11772 %   o  32-bit depth is reduced to 16.
11773 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11774 %      high byte and low byte are identical.
11775 %   o  Palette is sorted to remove unused entries and to put a
11776 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11777 %   o  Opaque matte channel is removed when writing an indexed PNG.
11778 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11779 %      this can be done without loss and a larger bit depth N was not
11780 %      requested via the "-define png:bit-depth=N" option.
11781 %   o  If matte channel is present but only one transparent color is
11782 %      present, RGB+tRNS is written instead of RGBA
11783 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11784 %      was requested when converting an opaque image).
11785 %
11786 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11787 */
11788 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11789   Image *image,ExceptionInfo *exception)
11790 {
11791   MagickBooleanType
11792     excluding,
11793     logging,
11794     status;
11795
11796   MngInfo
11797     *mng_info;
11798
11799   const char
11800     *value;
11801
11802   int
11803     source;
11804
11805   /*
11806     Open image file.
11807   */
11808   assert(image_info != (const ImageInfo *) NULL);
11809   assert(image_info->signature == MagickCoreSignature);
11810   assert(image != (Image *) NULL);
11811   assert(image->signature == MagickCoreSignature);
11812   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11813   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11814   /*
11815     Allocate a MngInfo structure.
11816   */
11817   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11818
11819   if (mng_info == (MngInfo *) NULL)
11820     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11821
11822   /*
11823     Initialize members of the MngInfo structure.
11824   */
11825   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11826   mng_info->image=image;
11827   mng_info->equal_backgrounds=MagickTrue;
11828
11829   /* See if user has requested a specific PNG subformat */
11830
11831   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11832   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11833   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11834   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11835   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11836
11837   value=GetImageOption(image_info,"png:format");
11838
11839   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11840     {
11841       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11842          "  Format=%s",value);
11843
11844       mng_info->write_png8 = MagickFalse;
11845       mng_info->write_png24 = MagickFalse;
11846       mng_info->write_png32 = MagickFalse;
11847       mng_info->write_png48 = MagickFalse;
11848       mng_info->write_png64 = MagickFalse;
11849
11850       if (LocaleCompare(value,"png8") == 0)
11851         mng_info->write_png8 = MagickTrue;
11852
11853       else if (LocaleCompare(value,"png24") == 0)
11854         mng_info->write_png24 = MagickTrue;
11855
11856       else if (LocaleCompare(value,"png32") == 0)
11857         mng_info->write_png32 = MagickTrue;
11858
11859       else if (LocaleCompare(value,"png48") == 0)
11860         mng_info->write_png48 = MagickTrue;
11861
11862       else if (LocaleCompare(value,"png64") == 0)
11863         mng_info->write_png64 = MagickTrue;
11864
11865       else if ((LocaleCompare(value,"png00") == 0) ||
11866          LocaleCompare(image_info->magick,"PNG00") == 0)
11867         {
11868           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11869           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11870
11871           if (value != (char *) NULL)
11872             {
11873               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11874                  "  png00 inherited bit depth=%s",value);
11875
11876               if (LocaleCompare(value,"1") == 0)
11877                 mng_info->write_png_depth = 1;
11878
11879               else if (LocaleCompare(value,"2") == 0)
11880                 mng_info->write_png_depth = 2;
11881
11882               else if (LocaleCompare(value,"4") == 0)
11883                 mng_info->write_png_depth = 4;
11884
11885               else if (LocaleCompare(value,"8") == 0)
11886                 mng_info->write_png_depth = 8;
11887
11888               else if (LocaleCompare(value,"16") == 0)
11889                 mng_info->write_png_depth = 16;
11890             }
11891
11892           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11893
11894           if (value != (char *) NULL)
11895             {
11896               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11897                  "  png00 inherited color type=%s",value);
11898
11899               if (LocaleCompare(value,"0") == 0)
11900                 mng_info->write_png_colortype = 1;
11901
11902               else if (LocaleCompare(value,"2") == 0)
11903                 mng_info->write_png_colortype = 3;
11904
11905               else if (LocaleCompare(value,"3") == 0)
11906                 mng_info->write_png_colortype = 4;
11907
11908               else if (LocaleCompare(value,"4") == 0)
11909                 mng_info->write_png_colortype = 5;
11910
11911               else if (LocaleCompare(value,"6") == 0)
11912                 mng_info->write_png_colortype = 7;
11913             }
11914         }
11915     }
11916
11917   if (mng_info->write_png8)
11918     {
11919       mng_info->write_png_colortype = /* 3 */ 4;
11920       mng_info->write_png_depth = 8;
11921       image->depth = 8;
11922     }
11923
11924   if (mng_info->write_png24)
11925     {
11926       mng_info->write_png_colortype = /* 2 */ 3;
11927       mng_info->write_png_depth = 8;
11928       image->depth = 8;
11929
11930       if (image->alpha_trait != UndefinedPixelTrait)
11931         (void) SetImageType(image,TrueColorAlphaType,exception);
11932
11933       else
11934         (void) SetImageType(image,TrueColorType,exception);
11935
11936       (void) SyncImage(image,exception);
11937     }
11938
11939   if (mng_info->write_png32)
11940     {
11941       mng_info->write_png_colortype = /* 6 */  7;
11942       mng_info->write_png_depth = 8;
11943       image->depth = 8;
11944       image->alpha_trait = BlendPixelTrait;
11945
11946       (void) SetImageType(image,TrueColorAlphaType,exception);
11947       (void) SyncImage(image,exception);
11948     }
11949
11950   if (mng_info->write_png48)
11951     {
11952       mng_info->write_png_colortype = /* 2 */ 3;
11953       mng_info->write_png_depth = 16;
11954       image->depth = 16;
11955
11956       if (image->alpha_trait != UndefinedPixelTrait)
11957         (void) SetImageType(image,TrueColorAlphaType,exception);
11958
11959       else
11960         (void) SetImageType(image,TrueColorType,exception);
11961
11962       (void) SyncImage(image,exception);
11963     }
11964
11965   if (mng_info->write_png64)
11966     {
11967       mng_info->write_png_colortype = /* 6 */  7;
11968       mng_info->write_png_depth = 16;
11969       image->depth = 16;
11970       image->alpha_trait = BlendPixelTrait;
11971
11972       (void) SetImageType(image,TrueColorAlphaType,exception);
11973       (void) SyncImage(image,exception);
11974     }
11975
11976   value=GetImageOption(image_info,"png:bit-depth");
11977
11978   if (value != (char *) NULL)
11979     {
11980       if (LocaleCompare(value,"1") == 0)
11981         mng_info->write_png_depth = 1;
11982
11983       else if (LocaleCompare(value,"2") == 0)
11984         mng_info->write_png_depth = 2;
11985
11986       else if (LocaleCompare(value,"4") == 0)
11987         mng_info->write_png_depth = 4;
11988
11989       else if (LocaleCompare(value,"8") == 0)
11990         mng_info->write_png_depth = 8;
11991
11992       else if (LocaleCompare(value,"16") == 0)
11993         mng_info->write_png_depth = 16;
11994
11995       else
11996         (void) ThrowMagickException(exception,
11997              GetMagickModule(),CoderWarning,
11998              "ignoring invalid defined png:bit-depth",
11999              "=%s",value);
12000
12001       if (logging != MagickFalse)
12002         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12003           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
12004     }
12005
12006   value=GetImageOption(image_info,"png:color-type");
12007
12008   if (value != (char *) NULL)
12009     {
12010       /* We must store colortype+1 because 0 is a valid colortype */
12011       if (LocaleCompare(value,"0") == 0)
12012         mng_info->write_png_colortype = 1;
12013
12014       else if (LocaleCompare(value,"1") == 0)
12015         mng_info->write_png_colortype = 2;
12016
12017       else if (LocaleCompare(value,"2") == 0)
12018         mng_info->write_png_colortype = 3;
12019
12020       else if (LocaleCompare(value,"3") == 0)
12021         mng_info->write_png_colortype = 4;
12022
12023       else if (LocaleCompare(value,"4") == 0)
12024         mng_info->write_png_colortype = 5;
12025
12026       else if (LocaleCompare(value,"6") == 0)
12027         mng_info->write_png_colortype = 7;
12028
12029       else
12030         (void) ThrowMagickException(exception,
12031              GetMagickModule(),CoderWarning,
12032              "ignoring invalid defined png:color-type",
12033              "=%s",value);
12034
12035       if (logging != MagickFalse)
12036         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12037           "  png:color-type=%d was defined.\n",
12038           mng_info->write_png_colortype-1);
12039     }
12040
12041   /* Check for chunks to be excluded:
12042    *
12043    * The default is to not exclude any known chunks except for any
12044    * listed in the "unused_chunks" array, above.
12045    *
12046    * Chunks can be listed for exclusion via a "png:exclude-chunk"
12047    * define (in the image properties or in the image artifacts)
12048    * or via a mng_info member.  For convenience, in addition
12049    * to or instead of a comma-separated list of chunks, the
12050    * "exclude-chunk" string can be simply "all" or "none".
12051    *
12052    * Note that the "-strip" option provides a convenient way of
12053    * doing the equivalent of
12054    *
12055    *    -define png:exclude-chunk="bKGD,caNv,cHRM,eXIf,gAMA,iCCP,
12056    *            iTXt,pHYs,sRGB,tEXt,zCCP,zTXt,date"
12057    *
12058    * The exclude-chunk define takes priority over the mng_info.
12059    *
12060    * A "png:include-chunk" define takes  priority over both the
12061    * mng_info and the "png:exclude-chunk" define.  Like the
12062    * "exclude-chunk" string, it can define "all" or "none" as
12063    * well as a comma-separated list.  Chunks that are unknown to
12064    * ImageMagick are always excluded, regardless of their "copy-safe"
12065    * status according to the PNG specification, and even if they
12066    * appear in the "include-chunk" list. Such defines appearing among
12067    * the image options take priority over those found among the image
12068    * artifacts.
12069    *
12070    * Finally, all chunks listed in the "unused_chunks" array are
12071    * automatically excluded, regardless of the other instructions
12072    * or lack thereof.
12073    *
12074    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
12075    * will not be written and the gAMA chunk will only be written if it
12076    * is not between .45 and .46, or approximately (1.0/2.2).
12077    *
12078    * If you exclude tRNS and the image has transparency, the colortype
12079    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
12080    *
12081    * The -strip option causes StripImage() to set the png:include-chunk
12082    * artifact to "none,trns,gama".
12083    */
12084
12085   mng_info->ping_exclude_bKGD=MagickFalse;
12086   mng_info->ping_exclude_caNv=MagickFalse;
12087   mng_info->ping_exclude_cHRM=MagickFalse;
12088   mng_info->ping_exclude_date=MagickFalse;
12089   mng_info->ping_exclude_eXIf=MagickFalse;
12090   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
12091   mng_info->ping_exclude_gAMA=MagickFalse;
12092   mng_info->ping_exclude_iCCP=MagickFalse;
12093   /* mng_info->ping_exclude_iTXt=MagickFalse; */
12094   mng_info->ping_exclude_oFFs=MagickFalse;
12095   mng_info->ping_exclude_pHYs=MagickFalse;
12096   mng_info->ping_exclude_sRGB=MagickFalse;
12097   mng_info->ping_exclude_tEXt=MagickFalse;
12098   mng_info->ping_exclude_tIME=MagickFalse;
12099   mng_info->ping_exclude_tRNS=MagickFalse;
12100   mng_info->ping_exclude_vpAg=MagickFalse;
12101   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
12102   mng_info->ping_exclude_zTXt=MagickFalse;
12103
12104   mng_info->ping_preserve_colormap=MagickFalse;
12105
12106   value=GetImageOption(image_info,"png:preserve-colormap");
12107   if (value == NULL)
12108      value=GetImageArtifact(image,"png:preserve-colormap");
12109   if (value != NULL)
12110      mng_info->ping_preserve_colormap=MagickTrue;
12111
12112   mng_info->ping_preserve_iCCP=MagickFalse;
12113
12114   value=GetImageOption(image_info,"png:preserve-iCCP");
12115   if (value == NULL)
12116      value=GetImageArtifact(image,"png:preserve-iCCP");
12117   if (value != NULL)
12118      mng_info->ping_preserve_iCCP=MagickTrue;
12119
12120   /* These compression-level, compression-strategy, and compression-filter
12121    * defines take precedence over values from the -quality option.
12122    */
12123   value=GetImageOption(image_info,"png:compression-level");
12124   if (value == NULL)
12125      value=GetImageArtifact(image,"png:compression-level");
12126   if (value != NULL)
12127   {
12128       /* We have to add 1 to everything because 0 is a valid input,
12129        * and we want to use 0 (the default) to mean undefined.
12130        */
12131       if (LocaleCompare(value,"0") == 0)
12132         mng_info->write_png_compression_level = 1;
12133
12134       else if (LocaleCompare(value,"1") == 0)
12135         mng_info->write_png_compression_level = 2;
12136
12137       else if (LocaleCompare(value,"2") == 0)
12138         mng_info->write_png_compression_level = 3;
12139
12140       else if (LocaleCompare(value,"3") == 0)
12141         mng_info->write_png_compression_level = 4;
12142
12143       else if (LocaleCompare(value,"4") == 0)
12144         mng_info->write_png_compression_level = 5;
12145
12146       else if (LocaleCompare(value,"5") == 0)
12147         mng_info->write_png_compression_level = 6;
12148
12149       else if (LocaleCompare(value,"6") == 0)
12150         mng_info->write_png_compression_level = 7;
12151
12152       else if (LocaleCompare(value,"7") == 0)
12153         mng_info->write_png_compression_level = 8;
12154
12155       else if (LocaleCompare(value,"8") == 0)
12156         mng_info->write_png_compression_level = 9;
12157
12158       else if (LocaleCompare(value,"9") == 0)
12159         mng_info->write_png_compression_level = 10;
12160
12161       else
12162         (void) ThrowMagickException(exception,
12163              GetMagickModule(),CoderWarning,
12164              "ignoring invalid defined png:compression-level",
12165              "=%s",value);
12166     }
12167
12168   value=GetImageOption(image_info,"png:compression-strategy");
12169   if (value == NULL)
12170      value=GetImageArtifact(image,"png:compression-strategy");
12171   if (value != NULL)
12172   {
12173       if (LocaleCompare(value,"0") == 0)
12174         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12175
12176       else if (LocaleCompare(value,"1") == 0)
12177         mng_info->write_png_compression_strategy = Z_FILTERED+1;
12178
12179       else if (LocaleCompare(value,"2") == 0)
12180         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
12181
12182       else if (LocaleCompare(value,"3") == 0)
12183 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
12184         mng_info->write_png_compression_strategy = Z_RLE+1;
12185 #else
12186         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12187 #endif
12188
12189       else if (LocaleCompare(value,"4") == 0)
12190 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
12191         mng_info->write_png_compression_strategy = Z_FIXED+1;
12192 #else
12193         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
12194 #endif
12195
12196       else
12197         (void) ThrowMagickException(exception,
12198              GetMagickModule(),CoderWarning,
12199              "ignoring invalid defined png:compression-strategy",
12200              "=%s",value);
12201     }
12202
12203   value=GetImageOption(image_info,"png:compression-filter");
12204   if (value == NULL)
12205      value=GetImageArtifact(image,"png:compression-filter");
12206   if (value != NULL)
12207   {
12208       /* To do: combinations of filters allowed by libpng
12209        * masks 0x08 through 0xf8
12210        *
12211        * Implement this as a comma-separated list of 0,1,2,3,4,5
12212        * where 5 is a special case meaning PNG_ALL_FILTERS.
12213        */
12214
12215       if (LocaleCompare(value,"0") == 0)
12216         mng_info->write_png_compression_filter = 1;
12217
12218       else if (LocaleCompare(value,"1") == 0)
12219         mng_info->write_png_compression_filter = 2;
12220
12221       else if (LocaleCompare(value,"2") == 0)
12222         mng_info->write_png_compression_filter = 3;
12223
12224       else if (LocaleCompare(value,"3") == 0)
12225         mng_info->write_png_compression_filter = 4;
12226
12227       else if (LocaleCompare(value,"4") == 0)
12228         mng_info->write_png_compression_filter = 5;
12229
12230       else if (LocaleCompare(value,"5") == 0)
12231         mng_info->write_png_compression_filter = 6;
12232
12233       else
12234         (void) ThrowMagickException(exception,
12235              GetMagickModule(),CoderWarning,
12236              "ignoring invalid defined png:compression-filter",
12237              "=%s",value);
12238   }
12239
12240   for (source=0; source<8; source++)
12241   {
12242     value = NULL;
12243
12244     if (source == 0)
12245       value=GetImageOption(image_info,"png:exclude-chunks");
12246
12247     if (source == 1)
12248       value=GetImageArtifact(image,"png:exclude-chunks");
12249
12250     if (source == 2)
12251       value=GetImageOption(image_info,"png:exclude-chunk");
12252
12253     if (source == 3)
12254       value=GetImageArtifact(image,"png:exclude-chunk");
12255
12256     if (source == 4)
12257       value=GetImageOption(image_info,"png:include-chunks");
12258
12259     if (source == 5)
12260       value=GetImageArtifact(image,"png:include-chunks");
12261
12262     if (source == 6)
12263       value=GetImageOption(image_info,"png:include-chunk");
12264
12265     if (source == 7)
12266       value=GetImageArtifact(image,"png:include-chunk");
12267
12268     if (value == NULL)
12269        continue;
12270
12271     if (source < 4)
12272       excluding = MagickTrue;
12273     else
12274       excluding = MagickFalse;
12275
12276     if (logging != MagickFalse)
12277       {
12278         if (source == 0 || source == 2)
12279            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12280               "  png:exclude-chunk=%s found in image options.\n", value);
12281         else if (source == 1 || source == 3)
12282            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12283               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12284         else if (source == 4 || source == 6)
12285            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12286               "  png:include-chunk=%s found in image options.\n", value);
12287         else /* if (source == 5 || source == 7) */
12288            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12289               "  png:include-chunk=%s found in image artifacts.\n", value);
12290       }
12291
12292     if (IsOptionMember("all",value) != MagickFalse)
12293       {
12294         mng_info->ping_exclude_bKGD=excluding;
12295         mng_info->ping_exclude_caNv=excluding;
12296         mng_info->ping_exclude_cHRM=excluding;
12297         mng_info->ping_exclude_date=excluding;
12298         mng_info->ping_exclude_EXIF=excluding;
12299         mng_info->ping_exclude_eXIf=excluding;
12300         mng_info->ping_exclude_gAMA=excluding;
12301         mng_info->ping_exclude_iCCP=excluding;
12302         /* mng_info->ping_exclude_iTXt=excluding; */
12303         mng_info->ping_exclude_oFFs=excluding;
12304         mng_info->ping_exclude_pHYs=excluding;
12305         mng_info->ping_exclude_sRGB=excluding;
12306         mng_info->ping_exclude_tEXt=excluding;
12307         mng_info->ping_exclude_tIME=excluding;
12308         mng_info->ping_exclude_tRNS=excluding;
12309         mng_info->ping_exclude_vpAg=excluding;
12310         mng_info->ping_exclude_zCCP=excluding;
12311         mng_info->ping_exclude_zTXt=excluding;
12312       }
12313
12314     if (IsOptionMember("none",value) != MagickFalse)
12315       {
12316         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12317           MagickTrue;
12318         mng_info->ping_exclude_caNv=excluding != MagickFalse ? MagickFalse :
12319           MagickTrue;
12320         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12321           MagickTrue;
12322         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12323           MagickTrue;
12324         mng_info->ping_exclude_eXIf=excluding != MagickFalse ? MagickFalse :
12325           MagickTrue;
12326         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12327           MagickTrue;
12328         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12329           MagickTrue;
12330         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12331           MagickTrue;
12332         /* mng_info->ping_exclude_iTXt=!excluding; */
12333         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12334           MagickTrue;
12335         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12336           MagickTrue;
12337         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12338           MagickTrue;
12339         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12340           MagickTrue;
12341         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12342           MagickTrue;
12343         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12344           MagickTrue;
12345         mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12346           MagickTrue;
12347         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12348           MagickTrue;
12349         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12350           MagickTrue;
12351       }
12352
12353     if (IsOptionMember("bkgd",value) != MagickFalse)
12354       mng_info->ping_exclude_bKGD=excluding;
12355
12356     if (IsOptionMember("caNv",value) != MagickFalse)
12357       mng_info->ping_exclude_caNv=excluding;
12358
12359     if (IsOptionMember("chrm",value) != MagickFalse)
12360       mng_info->ping_exclude_cHRM=excluding;
12361
12362     if (IsOptionMember("date",value) != MagickFalse)
12363       mng_info->ping_exclude_date=excluding;
12364
12365     if (IsOptionMember("exif",value) != MagickFalse)
12366       {
12367         mng_info->ping_exclude_EXIF=excluding;
12368         mng_info->ping_exclude_eXIf=excluding;
12369       }
12370
12371     if (IsOptionMember("gama",value) != MagickFalse)
12372       mng_info->ping_exclude_gAMA=excluding;
12373
12374     if (IsOptionMember("iccp",value) != MagickFalse)
12375       mng_info->ping_exclude_iCCP=excluding;
12376
12377 #if 0
12378     if (IsOptionMember("itxt",value) != MagickFalse)
12379       mng_info->ping_exclude_iTXt=excluding;
12380 #endif
12381
12382     if (IsOptionMember("offs",value) != MagickFalse)
12383       mng_info->ping_exclude_oFFs=excluding;
12384
12385     if (IsOptionMember("phys",value) != MagickFalse)
12386       mng_info->ping_exclude_pHYs=excluding;
12387
12388     if (IsOptionMember("srgb",value) != MagickFalse)
12389       mng_info->ping_exclude_sRGB=excluding;
12390
12391     if (IsOptionMember("text",value) != MagickFalse)
12392       mng_info->ping_exclude_tEXt=excluding;
12393
12394     if (IsOptionMember("time",value) != MagickFalse)
12395       mng_info->ping_exclude_tIME=excluding;
12396
12397     if (IsOptionMember("trns",value) != MagickFalse)
12398       mng_info->ping_exclude_tRNS=excluding;
12399
12400     if (IsOptionMember("vpag",value) != MagickFalse)
12401       mng_info->ping_exclude_vpAg=excluding;
12402
12403     if (IsOptionMember("zccp",value) != MagickFalse)
12404       mng_info->ping_exclude_zCCP=excluding;
12405
12406     if (IsOptionMember("ztxt",value) != MagickFalse)
12407       mng_info->ping_exclude_zTXt=excluding;
12408   }
12409
12410   if (logging != MagickFalse)
12411   {
12412     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12413       "  Chunks to be excluded from the output png:");
12414     if (mng_info->ping_exclude_bKGD != MagickFalse)
12415       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12416           "    bKGD");
12417     if (mng_info->ping_exclude_caNv != MagickFalse)
12418       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12419           "    caNv");
12420     if (mng_info->ping_exclude_cHRM != MagickFalse)
12421       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12422           "    cHRM");
12423     if (mng_info->ping_exclude_date != MagickFalse)
12424       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12425           "    date");
12426     if (mng_info->ping_exclude_EXIF != MagickFalse)
12427       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12428           "    EXIF");
12429     if (mng_info->ping_exclude_eXIf != MagickFalse)
12430       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12431           "    eXIf");
12432     if (mng_info->ping_exclude_gAMA != MagickFalse)
12433       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12434           "    gAMA");
12435     if (mng_info->ping_exclude_iCCP != MagickFalse)
12436       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12437           "    iCCP");
12438 #if 0
12439     if (mng_info->ping_exclude_iTXt != MagickFalse)
12440       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12441           "    iTXt");
12442 #endif
12443
12444     if (mng_info->ping_exclude_oFFs != MagickFalse)
12445       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12446           "    oFFs");
12447     if (mng_info->ping_exclude_pHYs != MagickFalse)
12448       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12449           "    pHYs");
12450     if (mng_info->ping_exclude_sRGB != MagickFalse)
12451       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12452           "    sRGB");
12453     if (mng_info->ping_exclude_tEXt != MagickFalse)
12454       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12455           "    tEXt");
12456     if (mng_info->ping_exclude_tIME != MagickFalse)
12457       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12458           "    tIME");
12459     if (mng_info->ping_exclude_tRNS != MagickFalse)
12460       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12461           "    tRNS");
12462     if (mng_info->ping_exclude_vpAg != MagickFalse)
12463       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12464           "    vpAg");
12465     if (mng_info->ping_exclude_zCCP != MagickFalse)
12466       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12467           "    zCCP");
12468     if (mng_info->ping_exclude_zTXt != MagickFalse)
12469       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12470           "    zTXt");
12471   }
12472
12473   mng_info->need_blob = MagickTrue;
12474
12475   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12476
12477   mng_info=MngInfoFreeStruct(mng_info);
12478
12479   if (logging != MagickFalse)
12480     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12481
12482   return(status);
12483 }
12484
12485 #if defined(JNG_SUPPORTED)
12486
12487 /* Write one JNG image */
12488 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12489    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12490 {
12491   Image
12492     *jpeg_image;
12493
12494   ImageInfo
12495     *jpeg_image_info;
12496
12497   MagickBooleanType
12498     logging,
12499     status;
12500
12501   size_t
12502     length;
12503
12504   unsigned char
12505     *blob,
12506     chunk[80],
12507     *p;
12508
12509   unsigned int
12510     jng_alpha_compression_method,
12511     jng_alpha_sample_depth,
12512     jng_color_type,
12513     transparent;
12514
12515   size_t
12516     jng_alpha_quality,
12517     jng_quality;
12518
12519   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12520     "  Enter WriteOneJNGImage()");
12521
12522   blob=(unsigned char *) NULL;
12523   jpeg_image=(Image *) NULL;
12524   jpeg_image_info=(ImageInfo *) NULL;
12525   length=0;
12526
12527   status=MagickTrue;
12528   transparent=image_info->type==GrayscaleAlphaType ||
12529      image_info->type==TrueColorAlphaType ||
12530      image->alpha_trait != UndefinedPixelTrait;
12531
12532   jng_alpha_sample_depth = 0;
12533
12534   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12535
12536   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12537
12538   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12539       image_info->quality;
12540
12541   if (jng_alpha_quality >= 1000)
12542     jng_alpha_quality /= 1000;
12543
12544   length=0;
12545
12546   if (transparent != 0)
12547     {
12548       jng_color_type=14;
12549
12550       /* Create JPEG blob, image, and image_info */
12551       if (logging != MagickFalse)
12552         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12553           "  Creating jpeg_image_info for alpha.");
12554
12555       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12556
12557       if (jpeg_image_info == (ImageInfo *) NULL)
12558         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12559
12560       if (logging != MagickFalse)
12561         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12562           "  Creating jpeg_image.");
12563
12564       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12565       if (jpeg_image == (Image *) NULL)
12566         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12567       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12568       jpeg_image->alpha_trait=UndefinedPixelTrait;
12569       jpeg_image->quality=jng_alpha_quality;
12570       jpeg_image_info->type=GrayscaleType;
12571       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12572       (void) AcquireUniqueFilename(jpeg_image->filename);
12573       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12574         "%s",jpeg_image->filename);
12575     }
12576   else
12577     {
12578       jng_alpha_compression_method=0;
12579       jng_color_type=10;
12580       jng_alpha_sample_depth=0;
12581     }
12582
12583   /* To do: check bit depth of PNG alpha channel */
12584
12585   /* Check if image is grayscale. */
12586   if (image_info->type != TrueColorAlphaType && image_info->type !=
12587     TrueColorType && SetImageGray(image,exception))
12588     jng_color_type-=2;
12589
12590   if (logging != MagickFalse)
12591     {
12592         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12593           "    JNG Quality           = %d",(int) jng_quality);
12594         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12595           "    JNG Color Type        = %d",jng_color_type);
12596         if (transparent != 0)
12597           {
12598             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12599               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12600             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12601               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12602             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12603               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12604           }
12605     }
12606
12607   if (transparent != 0)
12608     {
12609       if (jng_alpha_compression_method==0)
12610         {
12611           const char
12612             *value;
12613
12614           /* Encode alpha as a grayscale PNG blob */
12615           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12616             exception);
12617           if (status == MagickFalse)
12618             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12619
12620           if (logging != MagickFalse)
12621             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12622               "  Creating PNG blob.");
12623
12624           (void) CopyMagickString(jpeg_image_info->magick,"PNG",
12625              MagickPathExtent);
12626           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12627           jpeg_image_info->interlace=NoInterlace;
12628
12629           /* Exclude all ancillary chunks */
12630           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12631
12632           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12633             &length,exception);
12634
12635           /* Retrieve sample depth used */
12636           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12637           if (value != (char *) NULL)
12638             jng_alpha_sample_depth= (unsigned int) value[0];
12639         }
12640       else
12641         {
12642           /* Encode alpha as a grayscale JPEG blob */
12643
12644           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12645             exception);
12646           if (status == MagickFalse)
12647             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12648
12649           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",
12650             MagickPathExtent);
12651           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12652           jpeg_image_info->interlace=NoInterlace;
12653           if (logging != MagickFalse)
12654             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12655               "  Creating blob.");
12656           blob=(unsigned char *) ImageToBlob(jpeg_image_info,
12657              jpeg_image,&length,
12658            exception);
12659           jng_alpha_sample_depth=8;
12660
12661           if (logging != MagickFalse)
12662             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12663               "  Successfully read jpeg_image into a blob, length=%.20g.",
12664               (double) length);
12665
12666         }
12667       /* Destroy JPEG image and image_info */
12668       jpeg_image=DestroyImage(jpeg_image);
12669       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12670       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12671     }
12672
12673   /* Write JHDR chunk */
12674   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12675   PNGType(chunk,mng_JHDR);
12676   LogPNGChunk(logging,mng_JHDR,16L);
12677   PNGLong(chunk+4,(png_uint_32) image->columns);
12678   PNGLong(chunk+8,(png_uint_32) image->rows);
12679   chunk[12]=jng_color_type;
12680   chunk[13]=8;  /* sample depth */
12681   chunk[14]=8; /*jng_image_compression_method */
12682   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12683   chunk[16]=jng_alpha_sample_depth;
12684   chunk[17]=jng_alpha_compression_method;
12685   chunk[18]=0; /*jng_alpha_filter_method */
12686   chunk[19]=0; /*jng_alpha_interlace_method */
12687   (void) WriteBlob(image,20,chunk);
12688   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12689   if (logging != MagickFalse)
12690     {
12691       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12692         "    JNG width:%15lu",(unsigned long) image->columns);
12693
12694       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12695         "    JNG height:%14lu",(unsigned long) image->rows);
12696
12697       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12698         "    JNG color type:%10d",jng_color_type);
12699
12700       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12701         "    JNG sample depth:%8d",8);
12702
12703       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12704         "    JNG compression:%9d",8);
12705
12706       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12707         "    JNG interlace:%11d",0);
12708
12709       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12710         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12711
12712       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12713         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12714
12715       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716         "    JNG alpha filter:%8d",0);
12717
12718       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12719         "    JNG alpha interlace:%5d",0);
12720     }
12721
12722   /*
12723      Write leading ancillary chunks
12724   */
12725
12726   if (transparent != 0)
12727   {
12728     /*
12729       Write JNG bKGD chunk
12730     */
12731
12732     unsigned char
12733       blue,
12734       green,
12735       red;
12736
12737     ssize_t
12738       num_bytes;
12739
12740     if (jng_color_type == 8 || jng_color_type == 12)
12741       num_bytes=6L;
12742     else
12743       num_bytes=10L;
12744     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12745     PNGType(chunk,mng_bKGD);
12746     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12747     red=ScaleQuantumToChar(image->background_color.red);
12748     green=ScaleQuantumToChar(image->background_color.green);
12749     blue=ScaleQuantumToChar(image->background_color.blue);
12750     *(chunk+4)=0;
12751     *(chunk+5)=red;
12752     *(chunk+6)=0;
12753     *(chunk+7)=green;
12754     *(chunk+8)=0;
12755     *(chunk+9)=blue;
12756     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12757     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12758   }
12759
12760   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12761     {
12762       /*
12763         Write JNG sRGB chunk
12764       */
12765       (void) WriteBlobMSBULong(image,1L);
12766       PNGType(chunk,mng_sRGB);
12767       LogPNGChunk(logging,mng_sRGB,1L);
12768
12769       if (image->rendering_intent != UndefinedIntent)
12770         chunk[4]=(unsigned char)
12771           Magick_RenderingIntent_to_PNG_RenderingIntent(
12772           (image->rendering_intent));
12773
12774       else
12775         chunk[4]=(unsigned char)
12776           Magick_RenderingIntent_to_PNG_RenderingIntent(
12777           (PerceptualIntent));
12778
12779       (void) WriteBlob(image,5,chunk);
12780       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12781     }
12782   else
12783     {
12784       if (image->gamma != 0.0)
12785         {
12786           /*
12787              Write JNG gAMA chunk
12788           */
12789           (void) WriteBlobMSBULong(image,4L);
12790           PNGType(chunk,mng_gAMA);
12791           LogPNGChunk(logging,mng_gAMA,4L);
12792           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12793           (void) WriteBlob(image,8,chunk);
12794           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12795         }
12796
12797       if ((mng_info->equal_chrms == MagickFalse) &&
12798           (image->chromaticity.red_primary.x != 0.0))
12799         {
12800           PrimaryInfo
12801             primary;
12802
12803           /*
12804              Write JNG cHRM chunk
12805           */
12806           (void) WriteBlobMSBULong(image,32L);
12807           PNGType(chunk,mng_cHRM);
12808           LogPNGChunk(logging,mng_cHRM,32L);
12809           primary=image->chromaticity.white_point;
12810           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12811           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12812           primary=image->chromaticity.red_primary;
12813           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12814           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12815           primary=image->chromaticity.green_primary;
12816           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12817           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12818           primary=image->chromaticity.blue_primary;
12819           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12820           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12821           (void) WriteBlob(image,36,chunk);
12822           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12823         }
12824     }
12825
12826   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12827     {
12828       /*
12829          Write JNG pHYs chunk
12830       */
12831       (void) WriteBlobMSBULong(image,9L);
12832       PNGType(chunk,mng_pHYs);
12833       LogPNGChunk(logging,mng_pHYs,9L);
12834       if (image->units == PixelsPerInchResolution)
12835         {
12836           PNGLong(chunk+4,(png_uint_32)
12837             (image->resolution.x*100.0/2.54+0.5));
12838
12839           PNGLong(chunk+8,(png_uint_32)
12840             (image->resolution.y*100.0/2.54+0.5));
12841
12842           chunk[12]=1;
12843         }
12844
12845       else
12846         {
12847           if (image->units == PixelsPerCentimeterResolution)
12848             {
12849               PNGLong(chunk+4,(png_uint_32)
12850                 (image->resolution.x*100.0+0.5));
12851
12852               PNGLong(chunk+8,(png_uint_32)
12853                 (image->resolution.y*100.0+0.5));
12854
12855               chunk[12]=1;
12856             }
12857
12858           else
12859             {
12860               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12861               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12862               chunk[12]=0;
12863             }
12864         }
12865       (void) WriteBlob(image,13,chunk);
12866       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12867     }
12868
12869   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12870     {
12871       /*
12872          Write JNG oFFs chunk
12873       */
12874       (void) WriteBlobMSBULong(image,9L);
12875       PNGType(chunk,mng_oFFs);
12876       LogPNGChunk(logging,mng_oFFs,9L);
12877       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12878       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12879       chunk[12]=0;
12880       (void) WriteBlob(image,13,chunk);
12881       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12882     }
12883   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12884     {
12885        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12886        PNGType(chunk,mng_vpAg);
12887        LogPNGChunk(logging,mng_vpAg,9L);
12888        PNGLong(chunk+4,(png_uint_32) image->page.width);
12889        PNGLong(chunk+8,(png_uint_32) image->page.height);
12890        chunk[12]=0;   /* unit = pixels */
12891        (void) WriteBlob(image,13,chunk);
12892        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12893     }
12894
12895   if (transparent != 0)
12896     {
12897       if (jng_alpha_compression_method==0)
12898         {
12899           register ssize_t
12900             i;
12901
12902           size_t
12903             len;
12904
12905           /* Write IDAT chunk header */
12906           if (logging != MagickFalse)
12907             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12908               "  Write IDAT chunks from blob, length=%.20g.",(double)
12909               length);
12910
12911           /* Copy IDAT chunks */
12912           len=0;
12913           p=blob+8;
12914           for (i=8; i<(ssize_t) length; i+=len+12)
12915           {
12916             len=(((unsigned int) *(p    ) & 0xff) << 24) +
12917                 (((unsigned int) *(p + 1) & 0xff) << 16) +
12918                 (((unsigned int) *(p + 2) & 0xff) <<  8) +
12919                 (((unsigned int) *(p + 3) & 0xff)      ) ;
12920             p+=4;
12921
12922             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12923               {
12924                 /* Found an IDAT chunk. */
12925                 (void) WriteBlobMSBULong(image,len);
12926                 LogPNGChunk(logging,mng_IDAT,len);
12927                 (void) WriteBlob(image,len+4,p);
12928                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12929               }
12930
12931             else
12932               {
12933                 if (logging != MagickFalse)
12934                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12935                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12936                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12937               }
12938             p+=(8+len);
12939           }
12940         }
12941       else if (length != 0)
12942         {
12943           /* Write JDAA chunk header */
12944           if (logging != MagickFalse)
12945             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12946               "  Write JDAA chunk, length=%.20g.",(double) length);
12947           (void) WriteBlobMSBULong(image,(size_t) length);
12948           PNGType(chunk,mng_JDAA);
12949           LogPNGChunk(logging,mng_JDAA,length);
12950           /* Write JDAT chunk(s) data */
12951           (void) WriteBlob(image,4,chunk);
12952           (void) WriteBlob(image,length,blob);
12953           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12954              (uInt) length));
12955         }
12956       blob=(unsigned char *) RelinquishMagickMemory(blob);
12957     }
12958
12959   /* Encode image as a JPEG blob */
12960   if (logging != MagickFalse)
12961     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12962       "  Creating jpeg_image_info.");
12963   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12964   if (jpeg_image_info == (ImageInfo *) NULL)
12965     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12966
12967   if (logging != MagickFalse)
12968     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12969       "  Creating jpeg_image.");
12970
12971   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12972   if (jpeg_image == (Image *) NULL)
12973     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12974   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12975
12976   (void) AcquireUniqueFilename(jpeg_image->filename);
12977   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
12978     jpeg_image->filename);
12979
12980   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12981     exception);
12982
12983   if (logging != MagickFalse)
12984     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12985       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12986       (double) jpeg_image->rows);
12987
12988   if (status == MagickFalse)
12989     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12990
12991   if (jng_color_type == 8 || jng_color_type == 12)
12992     jpeg_image_info->type=GrayscaleType;
12993
12994   jpeg_image_info->quality=jng_quality;
12995   jpeg_image->quality=jng_quality;
12996   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12997   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12998
12999   if (logging != MagickFalse)
13000     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13001       "  Creating blob.");
13002
13003   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
13004     exception);
13005
13006   if (logging != MagickFalse)
13007     {
13008       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13009         "  Successfully read jpeg_image into a blob, length=%.20g.",
13010         (double) length);
13011
13012       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13013         "  Write JDAT chunk, length=%.20g.",(double) length);
13014     }
13015
13016   /* Write JDAT chunk(s) */
13017   (void) WriteBlobMSBULong(image,(size_t) length);
13018   PNGType(chunk,mng_JDAT);
13019   LogPNGChunk(logging,mng_JDAT,length);
13020   (void) WriteBlob(image,4,chunk);
13021   (void) WriteBlob(image,length,blob);
13022   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
13023
13024   jpeg_image=DestroyImage(jpeg_image);
13025   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
13026   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
13027   blob=(unsigned char *) RelinquishMagickMemory(blob);
13028
13029   /* Write IEND chunk */
13030   (void) WriteBlobMSBULong(image,0L);
13031   PNGType(chunk,mng_IEND);
13032   LogPNGChunk(logging,mng_IEND,0);
13033   (void) WriteBlob(image,4,chunk);
13034   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13035
13036   if (logging != MagickFalse)
13037     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13038       "  exit WriteOneJNGImage()");
13039
13040   return(status);
13041 }
13042
13043 /*
13044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13045 %                                                                             %
13046 %                                                                             %
13047 %                                                                             %
13048 %   W r i t e J N G I m a g e                                                 %
13049 %                                                                             %
13050 %                                                                             %
13051 %                                                                             %
13052 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13053 %
13054 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
13055 %
13056 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
13057 %
13058 %  The format of the WriteJNGImage method is:
13059 %
13060 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13061 %        Image *image,ExceptionInfo *exception)
13062 %
13063 %  A description of each parameter follows:
13064 %
13065 %    o image_info: the image info.
13066 %
13067 %    o image:  The image.
13068 %
13069 %    o exception: return any errors or warnings in this structure.
13070 %
13071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13072 */
13073 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
13074   Image *image, ExceptionInfo *exception)
13075 {
13076   MagickBooleanType
13077     logging,
13078     status;
13079
13080   MngInfo
13081     *mng_info;
13082
13083   /*
13084     Open image file.
13085   */
13086   assert(image_info != (const ImageInfo *) NULL);
13087   assert(image_info->signature == MagickCoreSignature);
13088   assert(image != (Image *) NULL);
13089   assert(image->signature == MagickCoreSignature);
13090   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13091   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
13092   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13093   if (status == MagickFalse)
13094     return(status);
13095   if ((image->columns > 65535UL) || (image->rows > 65535UL))
13096     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
13097
13098   /*
13099     Allocate a MngInfo structure.
13100   */
13101   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13102   if (mng_info == (MngInfo *) NULL)
13103     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13104   /*
13105     Initialize members of the MngInfo structure.
13106   */
13107   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13108   mng_info->image=image;
13109
13110   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
13111
13112   status=WriteOneJNGImage(mng_info,image_info,image,exception);
13113   mng_info=MngInfoFreeStruct(mng_info);
13114   (void) CloseBlob(image);
13115
13116   (void) CatchImageException(image);
13117   if (logging != MagickFalse)
13118     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
13119   return(status);
13120 }
13121 #endif
13122
13123 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
13124   Image *image, ExceptionInfo *exception)
13125 {
13126   Image
13127     *next_image;
13128
13129   MagickBooleanType
13130     status;
13131
13132   volatile MagickBooleanType
13133     logging;
13134
13135   MngInfo
13136     *mng_info;
13137
13138   int
13139     image_count,
13140     need_iterations,
13141     need_matte;
13142
13143   volatile int
13144 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13145     defined(PNG_MNG_FEATURES_SUPPORTED)
13146     need_local_plte,
13147 #endif
13148     all_images_are_gray,
13149     need_defi,
13150     use_global_plte;
13151
13152   register ssize_t
13153     i;
13154
13155   unsigned char
13156     chunk[800];
13157
13158   volatile unsigned int
13159     write_jng,
13160     write_mng;
13161
13162   volatile size_t
13163     scene;
13164
13165   size_t
13166     final_delay=0,
13167     initial_delay;
13168
13169 #if (PNG_LIBPNG_VER < 10200)
13170     if (image_info->verbose)
13171       printf("Your PNG library (libpng-%s) is rather old.\n",
13172          PNG_LIBPNG_VER_STRING);
13173 #endif
13174
13175   /*
13176     Open image file.
13177   */
13178   assert(image_info != (const ImageInfo *) NULL);
13179   assert(image_info->signature == MagickCoreSignature);
13180   assert(image != (Image *) NULL);
13181   assert(image->signature == MagickCoreSignature);
13182   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13183   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
13184   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
13185   if (status == MagickFalse)
13186     return(status);
13187
13188   /*
13189     Allocate a MngInfo structure.
13190   */
13191   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
13192   if (mng_info == (MngInfo *) NULL)
13193     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
13194   /*
13195     Initialize members of the MngInfo structure.
13196   */
13197   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
13198   mng_info->image=image;
13199   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
13200
13201   /*
13202    * See if user has requested a specific PNG subformat to be used
13203    * for all of the PNGs in the MNG being written, e.g.,
13204    *
13205    *    convert *.png png8:animation.mng
13206    *
13207    * To do: check -define png:bit_depth and png:color_type as well,
13208    * or perhaps use mng:bit_depth and mng:color_type instead for
13209    * global settings.
13210    */
13211
13212   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
13213   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
13214   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
13215
13216   write_jng=MagickFalse;
13217   if (image_info->compression == JPEGCompression)
13218     write_jng=MagickTrue;
13219
13220   mng_info->adjoin=image_info->adjoin &&
13221     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
13222
13223   if (logging != MagickFalse)
13224     {
13225       /* Log some info about the input */
13226       Image
13227         *p;
13228
13229       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13230         "  Checking input image(s)\n"
13231         "    Image_info depth: %.20g,    Type: %d",
13232         (double) image_info->depth, image_info->type);
13233
13234       scene=0;
13235       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
13236       {
13237
13238         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13239            "    Scene: %.20g\n,   Image depth: %.20g",
13240            (double) scene++, (double) p->depth);
13241
13242         if (p->alpha_trait != UndefinedPixelTrait)
13243           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13244             "      Matte: True");
13245
13246         else
13247           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13248             "      Matte: False");
13249
13250         if (p->storage_class == PseudoClass)
13251           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13252             "      Storage class: PseudoClass");
13253
13254         else
13255           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13256             "      Storage class: DirectClass");
13257
13258         if (p->colors)
13259           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13260             "      Number of colors: %.20g",(double) p->colors);
13261
13262         else
13263           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13264             "      Number of colors: unspecified");
13265
13266         if (mng_info->adjoin == MagickFalse)
13267           break;
13268       }
13269     }
13270
13271   use_global_plte=MagickFalse;
13272   all_images_are_gray=MagickFalse;
13273 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13274   need_local_plte=MagickTrue;
13275 #endif
13276   need_defi=MagickFalse;
13277   need_matte=MagickFalse;
13278   mng_info->framing_mode=1;
13279   mng_info->old_framing_mode=1;
13280
13281   if (write_mng)
13282       if (image_info->page != (char *) NULL)
13283         {
13284           /*
13285             Determine image bounding box.
13286           */
13287           SetGeometry(image,&mng_info->page);
13288           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13289             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13290         }
13291   if (write_mng)
13292     {
13293       unsigned int
13294         need_geom;
13295
13296       unsigned short
13297         red,
13298         green,
13299         blue;
13300
13301       const char *
13302         option;
13303
13304       mng_info->page=image->page;
13305       need_geom=MagickTrue;
13306       if (mng_info->page.width || mng_info->page.height)
13307          need_geom=MagickFalse;
13308       /*
13309         Check all the scenes.
13310       */
13311       initial_delay=image->delay;
13312       need_iterations=MagickFalse;
13313       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13314       mng_info->equal_physs=MagickTrue,
13315       mng_info->equal_gammas=MagickTrue;
13316       mng_info->equal_srgbs=MagickTrue;
13317       mng_info->equal_backgrounds=MagickTrue;
13318       image_count=0;
13319 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13320     defined(PNG_MNG_FEATURES_SUPPORTED)
13321       all_images_are_gray=MagickTrue;
13322       mng_info->equal_palettes=MagickFalse;
13323       need_local_plte=MagickFalse;
13324 #endif
13325       for (next_image=image; next_image != (Image *) NULL; )
13326       {
13327         if (need_geom)
13328           {
13329             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13330               mng_info->page.width=next_image->columns+next_image->page.x;
13331
13332             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13333               mng_info->page.height=next_image->rows+next_image->page.y;
13334           }
13335
13336         if (next_image->page.x || next_image->page.y)
13337           need_defi=MagickTrue;
13338
13339         if (next_image->alpha_trait != UndefinedPixelTrait)
13340           need_matte=MagickTrue;
13341
13342         if ((int) next_image->dispose >= BackgroundDispose)
13343           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13344                next_image->page.x || next_image->page.y ||
13345               ((next_image->columns < mng_info->page.width) &&
13346                (next_image->rows < mng_info->page.height)))
13347             mng_info->need_fram=MagickTrue;
13348
13349         if (next_image->iterations)
13350           need_iterations=MagickTrue;
13351
13352         final_delay=next_image->delay;
13353
13354         if (final_delay != initial_delay || final_delay > 1UL*
13355            next_image->ticks_per_second)
13356           mng_info->need_fram=1;
13357
13358 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13359     defined(PNG_MNG_FEATURES_SUPPORTED)
13360         /*
13361           check for global palette possibility.
13362         */
13363         if (image->alpha_trait != UndefinedPixelTrait)
13364            need_local_plte=MagickTrue;
13365
13366         if (need_local_plte == 0)
13367           {
13368             if (SetImageGray(image,exception) == MagickFalse)
13369               all_images_are_gray=MagickFalse;
13370             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13371             if (use_global_plte == 0)
13372               use_global_plte=mng_info->equal_palettes;
13373             need_local_plte=!mng_info->equal_palettes;
13374           }
13375 #endif
13376         if (GetNextImageInList(next_image) != (Image *) NULL)
13377           {
13378             if (next_image->background_color.red !=
13379                 next_image->next->background_color.red ||
13380                 next_image->background_color.green !=
13381                 next_image->next->background_color.green ||
13382                 next_image->background_color.blue !=
13383                 next_image->next->background_color.blue)
13384               mng_info->equal_backgrounds=MagickFalse;
13385
13386             if (next_image->gamma != next_image->next->gamma)
13387               mng_info->equal_gammas=MagickFalse;
13388
13389             if (next_image->rendering_intent !=
13390                 next_image->next->rendering_intent)
13391               mng_info->equal_srgbs=MagickFalse;
13392
13393             if ((next_image->units != next_image->next->units) ||
13394                 (next_image->resolution.x != next_image->next->resolution.x) ||
13395                 (next_image->resolution.y != next_image->next->resolution.y))
13396               mng_info->equal_physs=MagickFalse;
13397
13398             if (mng_info->equal_chrms)
13399               {
13400                 if (next_image->chromaticity.red_primary.x !=
13401                     next_image->next->chromaticity.red_primary.x ||
13402                     next_image->chromaticity.red_primary.y !=
13403                     next_image->next->chromaticity.red_primary.y ||
13404                     next_image->chromaticity.green_primary.x !=
13405                     next_image->next->chromaticity.green_primary.x ||
13406                     next_image->chromaticity.green_primary.y !=
13407                     next_image->next->chromaticity.green_primary.y ||
13408                     next_image->chromaticity.blue_primary.x !=
13409                     next_image->next->chromaticity.blue_primary.x ||
13410                     next_image->chromaticity.blue_primary.y !=
13411                     next_image->next->chromaticity.blue_primary.y ||
13412                     next_image->chromaticity.white_point.x !=
13413                     next_image->next->chromaticity.white_point.x ||
13414                     next_image->chromaticity.white_point.y !=
13415                     next_image->next->chromaticity.white_point.y)
13416                   mng_info->equal_chrms=MagickFalse;
13417               }
13418           }
13419         image_count++;
13420         next_image=GetNextImageInList(next_image);
13421       }
13422       if (image_count < 2)
13423         {
13424           mng_info->equal_backgrounds=MagickFalse;
13425           mng_info->equal_chrms=MagickFalse;
13426           mng_info->equal_gammas=MagickFalse;
13427           mng_info->equal_srgbs=MagickFalse;
13428           mng_info->equal_physs=MagickFalse;
13429           use_global_plte=MagickFalse;
13430 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13431           need_local_plte=MagickTrue;
13432 #endif
13433           need_iterations=MagickFalse;
13434         }
13435
13436      if (mng_info->need_fram == MagickFalse)
13437        {
13438          /*
13439            Only certain framing rates 100/n are exactly representable without
13440            the FRAM chunk but we'll allow some slop in VLC files
13441          */
13442          if (final_delay == 0)
13443            {
13444              if (need_iterations != MagickFalse)
13445                {
13446                  /*
13447                    It's probably a GIF with loop; don't run it *too* fast.
13448                  */
13449                  if (mng_info->adjoin)
13450                    {
13451                      final_delay=10;
13452                      (void) ThrowMagickException(exception,GetMagickModule(),
13453                        CoderWarning,
13454                        "input has zero delay between all frames; assuming",
13455                        " 10 cs `%s'","");
13456                    }
13457                }
13458              else
13459                mng_info->ticks_per_second=0;
13460            }
13461          if (final_delay != 0)
13462            mng_info->ticks_per_second=(png_uint_32)
13463               (image->ticks_per_second/final_delay);
13464          if (final_delay > 50)
13465            mng_info->ticks_per_second=2;
13466
13467          if (final_delay > 75)
13468            mng_info->ticks_per_second=1;
13469
13470          if (final_delay > 125)
13471            mng_info->need_fram=MagickTrue;
13472
13473          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13474             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13475             (final_delay != 25) && (final_delay != 50) &&
13476             (final_delay != (size_t) image->ticks_per_second))
13477            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13478        }
13479
13480      if (mng_info->need_fram != MagickFalse)
13481         mng_info->ticks_per_second=image->ticks_per_second;
13482      /*
13483         If pseudocolor, we should also check to see if all the
13484         palettes are identical and write a global PLTE if they are.
13485         ../glennrp Feb 99.
13486      */
13487      /*
13488         Write the MNG version 1.0 signature and MHDR chunk.
13489      */
13490      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13491      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13492      PNGType(chunk,mng_MHDR);
13493      LogPNGChunk(logging,mng_MHDR,28L);
13494      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13495      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13496      PNGLong(chunk+12,mng_info->ticks_per_second);
13497      PNGLong(chunk+16,0L);  /* layer count=unknown */
13498      PNGLong(chunk+20,0L);  /* frame count=unknown */
13499      PNGLong(chunk+24,0L);  /* play time=unknown   */
13500      if (write_jng)
13501        {
13502          if (need_matte)
13503            {
13504              if (need_defi || mng_info->need_fram || use_global_plte)
13505                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13506
13507              else
13508                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13509            }
13510
13511          else
13512            {
13513              if (need_defi || mng_info->need_fram || use_global_plte)
13514                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13515
13516              else
13517                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13518            }
13519        }
13520
13521      else
13522        {
13523          if (need_matte)
13524            {
13525              if (need_defi || mng_info->need_fram || use_global_plte)
13526                PNGLong(chunk+28,11L);    /* simplicity=LC */
13527
13528              else
13529                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13530            }
13531
13532          else
13533            {
13534              if (need_defi || mng_info->need_fram || use_global_plte)
13535                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13536
13537              else
13538                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13539            }
13540        }
13541      (void) WriteBlob(image,32,chunk);
13542      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13543      option=GetImageOption(image_info,"mng:need-cacheoff");
13544      if (option != (const char *) NULL)
13545        {
13546          size_t
13547            length;
13548          /*
13549            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13550          */
13551          PNGType(chunk,mng_nEED);
13552          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13553          (void) WriteBlobMSBULong(image,(size_t) length);
13554          LogPNGChunk(logging,mng_nEED,(size_t) length);
13555          length+=4;
13556          (void) WriteBlob(image,length,chunk);
13557          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13558        }
13559      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13560          (GetNextImageInList(image) != (Image *) NULL) &&
13561          (image->iterations != 1))
13562        {
13563          /*
13564            Write MNG TERM chunk
13565          */
13566          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13567          PNGType(chunk,mng_TERM);
13568          LogPNGChunk(logging,mng_TERM,10L);
13569          chunk[4]=3;  /* repeat animation */
13570          chunk[5]=0;  /* show last frame when done */
13571          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13572             final_delay/MagickMax(image->ticks_per_second,1)));
13573
13574          if (image->iterations == 0)
13575            PNGLong(chunk+10,PNG_UINT_31_MAX);
13576
13577          else
13578            PNGLong(chunk+10,(png_uint_32) image->iterations);
13579
13580          if (logging != MagickFalse)
13581            {
13582              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13583                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13584               final_delay/MagickMax(image->ticks_per_second,1)));
13585
13586              if (image->iterations == 0)
13587                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13588                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13589
13590              else
13591                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13592                  "     Image iterations: %.20g",(double) image->iterations);
13593            }
13594          (void) WriteBlob(image,14,chunk);
13595          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13596        }
13597      /*
13598        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13599      */
13600      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13601           mng_info->equal_srgbs)
13602        {
13603          /*
13604            Write MNG sRGB chunk
13605          */
13606          (void) WriteBlobMSBULong(image,1L);
13607          PNGType(chunk,mng_sRGB);
13608          LogPNGChunk(logging,mng_sRGB,1L);
13609
13610          if (image->rendering_intent != UndefinedIntent)
13611            chunk[4]=(unsigned char)
13612              Magick_RenderingIntent_to_PNG_RenderingIntent(
13613              (image->rendering_intent));
13614
13615          else
13616            chunk[4]=(unsigned char)
13617              Magick_RenderingIntent_to_PNG_RenderingIntent(
13618                (PerceptualIntent));
13619
13620          (void) WriteBlob(image,5,chunk);
13621          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13622          mng_info->have_write_global_srgb=MagickTrue;
13623        }
13624
13625      else
13626        {
13627          if (image->gamma && mng_info->equal_gammas)
13628            {
13629              /*
13630                 Write MNG gAMA chunk
13631              */
13632              (void) WriteBlobMSBULong(image,4L);
13633              PNGType(chunk,mng_gAMA);
13634              LogPNGChunk(logging,mng_gAMA,4L);
13635              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13636              (void) WriteBlob(image,8,chunk);
13637              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13638              mng_info->have_write_global_gama=MagickTrue;
13639            }
13640          if (mng_info->equal_chrms)
13641            {
13642              PrimaryInfo
13643                primary;
13644
13645              /*
13646                 Write MNG cHRM chunk
13647              */
13648              (void) WriteBlobMSBULong(image,32L);
13649              PNGType(chunk,mng_cHRM);
13650              LogPNGChunk(logging,mng_cHRM,32L);
13651              primary=image->chromaticity.white_point;
13652              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13653              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13654              primary=image->chromaticity.red_primary;
13655              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13656              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13657              primary=image->chromaticity.green_primary;
13658              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13659              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13660              primary=image->chromaticity.blue_primary;
13661              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13662              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13663              (void) WriteBlob(image,36,chunk);
13664              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13665              mng_info->have_write_global_chrm=MagickTrue;
13666            }
13667        }
13668      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13669        {
13670          /*
13671             Write MNG pHYs chunk
13672          */
13673          (void) WriteBlobMSBULong(image,9L);
13674          PNGType(chunk,mng_pHYs);
13675          LogPNGChunk(logging,mng_pHYs,9L);
13676
13677          if (image->units == PixelsPerInchResolution)
13678            {
13679              PNGLong(chunk+4,(png_uint_32)
13680                (image->resolution.x*100.0/2.54+0.5));
13681
13682              PNGLong(chunk+8,(png_uint_32)
13683                (image->resolution.y*100.0/2.54+0.5));
13684
13685              chunk[12]=1;
13686            }
13687
13688          else
13689            {
13690              if (image->units == PixelsPerCentimeterResolution)
13691                {
13692                  PNGLong(chunk+4,(png_uint_32)
13693                    (image->resolution.x*100.0+0.5));
13694
13695                  PNGLong(chunk+8,(png_uint_32)
13696                    (image->resolution.y*100.0+0.5));
13697
13698                  chunk[12]=1;
13699                }
13700
13701              else
13702                {
13703                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13704                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13705                  chunk[12]=0;
13706                }
13707            }
13708          (void) WriteBlob(image,13,chunk);
13709          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13710        }
13711      /*
13712        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13713        or does not cover the entire frame.
13714      */
13715      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13716          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13717          (image->page.width+image->page.x < mng_info->page.width))
13718          || (image->page.height && (image->page.height+image->page.y
13719          < mng_info->page.height))))
13720        {
13721          (void) WriteBlobMSBULong(image,6L);
13722          PNGType(chunk,mng_BACK);
13723          LogPNGChunk(logging,mng_BACK,6L);
13724          red=ScaleQuantumToShort(image->background_color.red);
13725          green=ScaleQuantumToShort(image->background_color.green);
13726          blue=ScaleQuantumToShort(image->background_color.blue);
13727          PNGShort(chunk+4,red);
13728          PNGShort(chunk+6,green);
13729          PNGShort(chunk+8,blue);
13730          (void) WriteBlob(image,10,chunk);
13731          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13732          if (mng_info->equal_backgrounds)
13733            {
13734              (void) WriteBlobMSBULong(image,6L);
13735              PNGType(chunk,mng_bKGD);
13736              LogPNGChunk(logging,mng_bKGD,6L);
13737              (void) WriteBlob(image,10,chunk);
13738              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13739            }
13740        }
13741
13742 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13743      if ((need_local_plte == MagickFalse) &&
13744          (image->storage_class == PseudoClass) &&
13745          (all_images_are_gray == MagickFalse))
13746        {
13747          size_t
13748            data_length;
13749
13750          /*
13751            Write MNG PLTE chunk
13752          */
13753          data_length=3*image->colors;
13754          (void) WriteBlobMSBULong(image,data_length);
13755          PNGType(chunk,mng_PLTE);
13756          LogPNGChunk(logging,mng_PLTE,data_length);
13757
13758          for (i=0; i < (ssize_t) image->colors; i++)
13759          {
13760            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13761              image->colormap[i].red) & 0xff);
13762            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13763              image->colormap[i].green) & 0xff);
13764            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13765              image->colormap[i].blue) & 0xff);
13766          }
13767
13768          (void) WriteBlob(image,data_length+4,chunk);
13769          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13770          mng_info->have_write_global_plte=MagickTrue;
13771        }
13772 #endif
13773     }
13774   scene=0;
13775   mng_info->delay=0;
13776 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13777     defined(PNG_MNG_FEATURES_SUPPORTED)
13778   mng_info->equal_palettes=MagickFalse;
13779 #endif
13780   do
13781   {
13782     if (mng_info->adjoin)
13783     {
13784 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13785     defined(PNG_MNG_FEATURES_SUPPORTED)
13786     /*
13787       If we aren't using a global palette for the entire MNG, check to
13788       see if we can use one for two or more consecutive images.
13789     */
13790     if (need_local_plte && use_global_plte && !all_images_are_gray)
13791       {
13792         if (mng_info->IsPalette)
13793           {
13794             /*
13795               When equal_palettes is true, this image has the same palette
13796               as the previous PseudoClass image
13797             */
13798             mng_info->have_write_global_plte=mng_info->equal_palettes;
13799             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13800             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13801               {
13802                 /*
13803                   Write MNG PLTE chunk
13804                 */
13805                 size_t
13806                   data_length;
13807
13808                 data_length=3*image->colors;
13809                 (void) WriteBlobMSBULong(image,data_length);
13810                 PNGType(chunk,mng_PLTE);
13811                 LogPNGChunk(logging,mng_PLTE,data_length);
13812
13813                 for (i=0; i < (ssize_t) image->colors; i++)
13814                 {
13815                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13816                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13817                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13818                 }
13819
13820                 (void) WriteBlob(image,data_length+4,chunk);
13821                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13822                    (uInt) (data_length+4)));
13823                 mng_info->have_write_global_plte=MagickTrue;
13824               }
13825           }
13826         else
13827           mng_info->have_write_global_plte=MagickFalse;
13828       }
13829 #endif
13830     if (need_defi)
13831       {
13832         ssize_t
13833           previous_x,
13834           previous_y;
13835
13836         if (scene)
13837           {
13838             previous_x=mng_info->page.x;
13839             previous_y=mng_info->page.y;
13840           }
13841         else
13842           {
13843             previous_x=0;
13844             previous_y=0;
13845           }
13846         mng_info->page=image->page;
13847         if ((mng_info->page.x !=  previous_x) ||
13848             (mng_info->page.y != previous_y))
13849           {
13850              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13851              PNGType(chunk,mng_DEFI);
13852              LogPNGChunk(logging,mng_DEFI,12L);
13853              chunk[4]=0; /* object 0 MSB */
13854              chunk[5]=0; /* object 0 LSB */
13855              chunk[6]=0; /* visible  */
13856              chunk[7]=0; /* abstract */
13857              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13858              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13859              (void) WriteBlob(image,16,chunk);
13860              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13861           }
13862       }
13863     }
13864
13865    mng_info->write_mng=write_mng;
13866
13867    if ((int) image->dispose >= 3)
13868      mng_info->framing_mode=3;
13869
13870    if (mng_info->need_fram && mng_info->adjoin &&
13871        ((image->delay != mng_info->delay) ||
13872         (mng_info->framing_mode != mng_info->old_framing_mode)))
13873      {
13874        if (image->delay == mng_info->delay)
13875          {
13876            /*
13877              Write a MNG FRAM chunk with the new framing mode.
13878            */
13879            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13880            PNGType(chunk,mng_FRAM);
13881            LogPNGChunk(logging,mng_FRAM,1L);
13882            chunk[4]=(unsigned char) mng_info->framing_mode;
13883            (void) WriteBlob(image,5,chunk);
13884            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13885          }
13886        else
13887          {
13888            /*
13889              Write a MNG FRAM chunk with the delay.
13890            */
13891            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13892            PNGType(chunk,mng_FRAM);
13893            LogPNGChunk(logging,mng_FRAM,10L);
13894            chunk[4]=(unsigned char) mng_info->framing_mode;
13895            chunk[5]=0;  /* frame name separator (no name) */
13896            chunk[6]=2;  /* flag for changing default delay */
13897            chunk[7]=0;  /* flag for changing frame timeout */
13898            chunk[8]=0;  /* flag for changing frame clipping */
13899            chunk[9]=0;  /* flag for changing frame sync_id */
13900            PNGLong(chunk+10,(png_uint_32)
13901              ((mng_info->ticks_per_second*
13902              image->delay)/MagickMax(image->ticks_per_second,1)));
13903            (void) WriteBlob(image,14,chunk);
13904            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13905            mng_info->delay=(png_uint_32) image->delay;
13906          }
13907        mng_info->old_framing_mode=mng_info->framing_mode;
13908      }
13909
13910 #if defined(JNG_SUPPORTED)
13911    if (image_info->compression == JPEGCompression)
13912      {
13913        ImageInfo
13914          *write_info;
13915
13916        if (logging != MagickFalse)
13917          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13918            "  Writing JNG object.");
13919        /* To do: specify the desired alpha compression method. */
13920        write_info=CloneImageInfo(image_info);
13921        write_info->compression=UndefinedCompression;
13922        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13923        write_info=DestroyImageInfo(write_info);
13924      }
13925    else
13926 #endif
13927      {
13928        if (logging != MagickFalse)
13929          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13930            "  Writing PNG object.");
13931
13932        mng_info->need_blob = MagickFalse;
13933        mng_info->ping_preserve_colormap = MagickFalse;
13934
13935        /* We don't want any ancillary chunks written */
13936        mng_info->ping_exclude_bKGD=MagickTrue;
13937        mng_info->ping_exclude_caNv=MagickTrue;
13938        mng_info->ping_exclude_cHRM=MagickTrue;
13939        mng_info->ping_exclude_date=MagickTrue;
13940        mng_info->ping_exclude_EXIF=MagickTrue;
13941        mng_info->ping_exclude_gAMA=MagickTrue;
13942        mng_info->ping_exclude_iCCP=MagickTrue;
13943        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13944        mng_info->ping_exclude_oFFs=MagickTrue;
13945        mng_info->ping_exclude_pHYs=MagickTrue;
13946        mng_info->ping_exclude_sRGB=MagickTrue;
13947        mng_info->ping_exclude_tEXt=MagickTrue;
13948        mng_info->ping_exclude_tRNS=MagickTrue;
13949        mng_info->ping_exclude_vpAg=MagickTrue;
13950        mng_info->ping_exclude_zCCP=MagickTrue;
13951        mng_info->ping_exclude_zTXt=MagickTrue;
13952
13953        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13954      }
13955
13956     if (status == MagickFalse)
13957       {
13958         mng_info=MngInfoFreeStruct(mng_info);
13959         (void) CloseBlob(image);
13960         return(MagickFalse);
13961       }
13962     (void) CatchImageException(image);
13963     if (GetNextImageInList(image) == (Image *) NULL)
13964       break;
13965     image=SyncNextImageInList(image);
13966     status=SetImageProgress(image,SaveImagesTag,scene++,
13967       GetImageListLength(image));
13968
13969     if (status == MagickFalse)
13970       break;
13971
13972   } while (mng_info->adjoin);
13973
13974   if (write_mng)
13975     {
13976       while (GetPreviousImageInList(image) != (Image *) NULL)
13977         image=GetPreviousImageInList(image);
13978       /*
13979         Write the MEND chunk.
13980       */
13981       (void) WriteBlobMSBULong(image,0x00000000L);
13982       PNGType(chunk,mng_MEND);
13983       LogPNGChunk(logging,mng_MEND,0L);
13984       (void) WriteBlob(image,4,chunk);
13985       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13986     }
13987   /*
13988     Relinquish resources.
13989   */
13990   (void) CloseBlob(image);
13991   mng_info=MngInfoFreeStruct(mng_info);
13992
13993   if (logging != MagickFalse)
13994     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13995
13996   return(MagickTrue);
13997 }
13998 #else /* PNG_LIBPNG_VER > 10011 */
13999
14000 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
14001   Image *image)
14002 {
14003   (void) image;
14004   printf("Your PNG library is too old: You have libpng-%s\n",
14005      PNG_LIBPNG_VER_STRING);
14006
14007   ThrowBinaryException(CoderError,"PNG library is too old",
14008      image_info->filename);
14009 }
14010
14011 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
14012   Image *image)
14013 {
14014   return(WritePNGImage(image_info,image));
14015 }
14016 #endif /* PNG_LIBPNG_VER > 10011 */
14017 #endif