]> granicus.if.org Git - imagemagick/blob - coders/png.c
Fix improper cast that could cause an overflow as demonstrated in #347.
[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 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39
40 \f
41 /*
42   Include declarations.
43 */
44 #include "MagickCore/studio.h"
45 #include "MagickCore/artifact.h"
46 #include "MagickCore/attribute.h"
47 #include "MagickCore/blob.h"
48 #include "MagickCore/blob-private.h"
49 #include "MagickCore/cache.h"
50 #include "MagickCore/channel.h"
51 #include "MagickCore/color.h"
52 #include "MagickCore/color-private.h"
53 #include "MagickCore/colormap.h"
54 #include "MagickCore/colorspace.h"
55 #include "MagickCore/colorspace-private.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/histogram.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/layer.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/MagickCore.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/statistic.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/utility.h"
87 #if defined(MAGICKCORE_PNG_DELEGATE)
88
89 /* Suppress libpng pedantic warnings that were added in
90  * libpng-1.2.41 and libpng-1.4.0.  If you are working on
91  * migration to libpng-1.5, remove these defines and then
92  * fix any code that generates warnings.
93  */
94 /* #define PNG_DEPRECATED   Use of this function is deprecated */
95 /* #define PNG_USE_RESULT   The result of this function must be checked */
96 /* #define PNG_NORETURN     This function does not return */
97 /* #define PNG_ALLOCATED    The result of the function is new memory */
98 /* #define PNG_DEPSTRUCT    Access to this struct member is deprecated */
99
100 /* PNG_PTR_NORETURN does not work on some platforms, in libpng-1.5.x */
101 #define PNG_PTR_NORETURN
102
103 #include "png.h"
104 #include "zlib.h"
105 \f
106 /* ImageMagick differences */
107 #define first_scene scene
108
109 #if PNG_LIBPNG_VER > 10011
110 /*
111   Optional declarations. Define or undefine them as you like.
112 */
113 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
114
115 /*
116   Features under construction.  Define these to work on them.
117 */
118 #undef MNG_OBJECT_BUFFERS
119 #undef MNG_BASI_SUPPORTED
120 #define MNG_COALESCE_LAYERS /* In 5.4.4, this interfered with MMAP'ed files. */
121 #define MNG_INSERT_LAYERS   /* Troublesome, but seem to work as of 5.4.4 */
122 #if defined(MAGICKCORE_JPEG_DELEGATE)
123 #  define JNG_SUPPORTED /* Not finished as of 5.5.2.  See "To do" comments. */
124 #endif
125 #if !defined(RGBColorMatchExact)
126 #define IsPNGColorEqual(color,target) \
127        (((color).red == (target).red) && \
128         ((color).green == (target).green) && \
129         ((color).blue == (target).blue))
130 #endif
131
132 /* Table of recognized sRGB ICC profiles */
133 struct sRGB_info_struct
134 {
135     png_uint_32 len;
136     png_uint_32 crc;
137     png_byte intent;
138 };
139
140 const struct sRGB_info_struct sRGB_info[] =
141 {
142     /* ICC v2 perceptual sRGB_IEC61966-2-1_black_scaled.icc */
143     { 3048, 0x3b8772b9UL, 0},
144
145     /* ICC v2 relative sRGB_IEC61966-2-1_no_black_scaling.icc */
146     { 3052, 0x427ebb21UL, 1},
147
148     /* ICC v4 perceptual sRGB_v4_ICC_preference_displayclass.icc */
149     {60988, 0x306fd8aeUL, 0},
150
151     /* ICC v4 perceptual sRGB_v4_ICC_preference.icc perceptual */
152      {60960, 0xbbef7812UL, 0},
153
154     /* HP? sRGB v2 media-relative sRGB_IEC61966-2-1_noBPC.icc */
155      { 3024, 0x5d5129ceUL, 1},
156
157      /* HP-Microsoft sRGB v2 perceptual */
158      { 3144, 0x182ea552UL, 0},
159
160      /* HP-Microsoft sRGB v2 media-relative */
161      { 3144, 0xf29e526dUL, 1},
162
163      /* Facebook's "2012/01/25 03:41:57", 524, "TINYsRGB.icc" */
164      {  524, 0xd4938c39UL, 0},
165
166      /* "2012/11/28 22:35:21", 3212, "Argyll_sRGB.icm") */
167      { 3212, 0x034af5a1UL, 0},
168
169      /* Not recognized */
170      {    0, 0x00000000UL, 0},
171 };
172
173 /* Macros for left-bit-replication to ensure that pixels
174  * and PixelInfos all have the same image->depth, and for use
175  * in PNG8 quantization.
176  */
177
178 /* LBR01: Replicate top bit */
179
180 #define LBR01PacketRed(pixelpacket) \
181      (pixelpacket).red=(ScaleQuantumToChar((pixelpacket).red) < 0x10 ? \
182         0 : QuantumRange);
183
184 #define LBR01PacketGreen(pixelpacket) \
185      (pixelpacket).green=(ScaleQuantumToChar((pixelpacket).green) < 0x10 ? \
186         0 : QuantumRange);
187
188 #define LBR01PacketBlue(pixelpacket) \
189      (pixelpacket).blue=(ScaleQuantumToChar((pixelpacket).blue) < 0x10 ? \
190         0 : QuantumRange);
191
192 #define LBR01PacketAlpha(pixelpacket) \
193      (pixelpacket).alpha=(ScaleQuantumToChar((pixelpacket).alpha) < 0x10 ? \
194         0 : QuantumRange);
195
196 #define LBR01PacketRGB(pixelpacket) \
197         { \
198         LBR01PacketRed((pixelpacket)); \
199         LBR01PacketGreen((pixelpacket)); \
200         LBR01PacketBlue((pixelpacket)); \
201         }
202
203 #define LBR01PacketRGBO(pixelpacket) \
204         { \
205         LBR01PacketRGB((pixelpacket)); \
206         LBR01PacketAlpha((pixelpacket)); \
207         }
208
209 #define LBR01PixelRed(pixel) \
210         (SetPixelRed(image, \
211         ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
212         0 : QuantumRange,(pixel)));
213
214 #define LBR01PixelGreen(pixel) \
215         (SetPixelGreen(image, \
216         ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
217         0 : QuantumRange,(pixel)));
218
219 #define LBR01PixelBlue(pixel) \
220         (SetPixelBlue(image, \
221         ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
222         0 : QuantumRange,(pixel)));
223
224 #define LBR01PixelAlpha(pixel) \
225         (SetPixelAlpha(image, \
226         ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
227         0 : QuantumRange,(pixel)));
228
229 #define LBR01PixelRGB(pixel) \
230         { \
231         LBR01PixelRed((pixel)); \
232         LBR01PixelGreen((pixel)); \
233         LBR01PixelBlue((pixel)); \
234         }
235
236 #define LBR01PixelRGBA(pixel) \
237         { \
238         LBR01PixelRGB((pixel)); \
239         LBR01PixelAlpha((pixel)); \
240         }
241
242 /* LBR02: Replicate top 2 bits */
243
244 #define LBR02PacketRed(pixelpacket) \
245    { \
246      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xc0; \
247      (pixelpacket).red=ScaleCharToQuantum( \
248        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
249    }
250 #define LBR02PacketGreen(pixelpacket) \
251    { \
252      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xc0; \
253      (pixelpacket).green=ScaleCharToQuantum( \
254        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
255    }
256 #define LBR02PacketBlue(pixelpacket) \
257    { \
258      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xc0; \
259      (pixelpacket).blue=ScaleCharToQuantum( \
260        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
261    }
262 #define LBR02PacketAlpha(pixelpacket) \
263    { \
264      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xc0; \
265      (pixelpacket).alpha=ScaleCharToQuantum( \
266        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))); \
267    }
268
269 #define LBR02PacketRGB(pixelpacket) \
270         { \
271         LBR02PacketRed((pixelpacket)); \
272         LBR02PacketGreen((pixelpacket)); \
273         LBR02PacketBlue((pixelpacket)); \
274         }
275
276 #define LBR02PacketRGBO(pixelpacket) \
277         { \
278         LBR02PacketRGB((pixelpacket)); \
279         LBR02PacketAlpha((pixelpacket)); \
280         }
281
282 #define LBR02PixelRed(pixel) \
283    { \
284      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
285        & 0xc0; \
286      SetPixelRed(image, ScaleCharToQuantum( \
287        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
288        (pixel)); \
289    }
290 #define LBR02PixelGreen(pixel) \
291    { \
292      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
293        & 0xc0; \
294      SetPixelGreen(image, ScaleCharToQuantum( \
295        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
296        (pixel)); \
297    }
298 #define LBR02PixelBlue(pixel) \
299    { \
300      unsigned char lbr_bits= \
301        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xc0; \
302      SetPixelBlue(image, ScaleCharToQuantum( \
303        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
304        (pixel)); \
305    }
306 #define LBR02PixelAlpha(pixel) \
307    { \
308      unsigned char lbr_bits= \
309        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xc0; \
310      SetPixelAlpha(image, ScaleCharToQuantum( \
311        (lbr_bits | (lbr_bits >> 2) | (lbr_bits >> 4) | (lbr_bits >> 6))), \
312        (pixel) ); \
313    }
314
315 #define LBR02PixelRGB(pixel) \
316         { \
317         LBR02PixelRed((pixel)); \
318         LBR02PixelGreen((pixel)); \
319         LBR02PixelBlue((pixel)); \
320         }
321
322 #define LBR02PixelRGBA(pixel) \
323         { \
324         LBR02PixelRGB((pixel)); \
325         LBR02PixelAlpha((pixel)); \
326         }
327
328 /* LBR03: Replicate top 3 bits (only used with opaque pixels during
329    PNG8 quantization) */
330
331 #define LBR03PacketRed(pixelpacket) \
332    { \
333      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xe0; \
334      (pixelpacket).red=ScaleCharToQuantum( \
335        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
336    }
337 #define LBR03PacketGreen(pixelpacket) \
338    { \
339      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xe0; \
340      (pixelpacket).green=ScaleCharToQuantum( \
341        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
342    }
343 #define LBR03PacketBlue(pixelpacket) \
344    { \
345      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xe0; \
346      (pixelpacket).blue=ScaleCharToQuantum( \
347        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))); \
348    }
349
350 #define LBR03PacketRGB(pixelpacket) \
351         { \
352         LBR03PacketRed((pixelpacket)); \
353         LBR03PacketGreen((pixelpacket)); \
354         LBR03PacketBlue((pixelpacket)); \
355         }
356
357 #define LBR03PixelRed(pixel) \
358    { \
359      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
360        & 0xe0; \
361      SetPixelRed(image, ScaleCharToQuantum( \
362        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
363    }
364 #define LBR03Green(pixel) \
365    { \
366      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
367        & 0xe0; \
368      SetPixelGreen(image, ScaleCharToQuantum( \
369        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
370    }
371 #define LBR03Blue(pixel) \
372    { \
373      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelBlue(image,(pixel))) \
374        & 0xe0; \
375      SetPixelBlue(image, ScaleCharToQuantum( \
376        (lbr_bits | (lbr_bits >> 3) | (lbr_bits >> 6))), (pixel)); \
377    }
378
379 #define LBR03RGB(pixel) \
380         { \
381         LBR03PixelRed((pixel)); \
382         LBR03Green((pixel)); \
383         LBR03Blue((pixel)); \
384         }
385
386 /* LBR04: Replicate top 4 bits */
387
388 #define LBR04PacketRed(pixelpacket) \
389    { \
390      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).red) & 0xf0; \
391      (pixelpacket).red=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
392    }
393 #define LBR04PacketGreen(pixelpacket) \
394    { \
395      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).green) & 0xf0; \
396      (pixelpacket).green=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
397    }
398 #define LBR04PacketBlue(pixelpacket) \
399    { \
400      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).blue) & 0xf0; \
401      (pixelpacket).blue=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
402    }
403 #define LBR04PacketAlpha(pixelpacket) \
404    { \
405      unsigned char lbr_bits=ScaleQuantumToChar((pixelpacket).alpha) & 0xf0; \
406      (pixelpacket).alpha=ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))); \
407    }
408
409 #define LBR04PacketRGB(pixelpacket) \
410         { \
411         LBR04PacketRed((pixelpacket)); \
412         LBR04PacketGreen((pixelpacket)); \
413         LBR04PacketBlue((pixelpacket)); \
414         }
415
416 #define LBR04PacketRGBO(pixelpacket) \
417         { \
418         LBR04PacketRGB((pixelpacket)); \
419         LBR04PacketAlpha((pixelpacket)); \
420         }
421
422 #define LBR04PixelRed(pixel) \
423    { \
424      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelRed(image,(pixel))) \
425        & 0xf0; \
426      SetPixelRed(image,\
427        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
428    }
429 #define LBR04PixelGreen(pixel) \
430    { \
431      unsigned char lbr_bits=ScaleQuantumToChar(GetPixelGreen(image,(pixel)))\
432        & 0xf0; \
433      SetPixelGreen(image,\
434        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
435    }
436 #define LBR04PixelBlue(pixel) \
437    { \
438      unsigned char lbr_bits= \
439        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) & 0xf0; \
440      SetPixelBlue(image,\
441        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
442    }
443 #define LBR04PixelAlpha(pixel) \
444    { \
445      unsigned char lbr_bits= \
446        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) & 0xf0; \
447      SetPixelAlpha(image,\
448        ScaleCharToQuantum((lbr_bits | (lbr_bits >> 4))), (pixel)); \
449    }
450
451 #define LBR04PixelRGB(pixel) \
452         { \
453         LBR04PixelRed((pixel)); \
454         LBR04PixelGreen((pixel)); \
455         LBR04PixelBlue((pixel)); \
456         }
457
458 #define LBR04PixelRGBA(pixel) \
459         { \
460         LBR04PixelRGB((pixel)); \
461         LBR04PixelAlpha((pixel)); \
462         }
463
464 /*
465   Establish thread safety.
466   setjmp/longjmp is claimed to be safe on these platforms:
467   setjmp/longjmp is alleged to be unsafe on these platforms:
468 */
469 #ifdef PNG_SETJMP_SUPPORTED
470 # ifndef IMPNG_SETJMP_IS_THREAD_SAFE
471 #   define IMPNG_SETJMP_NOT_THREAD_SAFE
472 # endif
473
474 # ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
475 static SemaphoreInfo
476   *ping_semaphore = (SemaphoreInfo *) NULL;
477 # endif
478 #endif
479
480 /*
481   This temporary until I set up malloc'ed object attributes array.
482   Recompile with MNG_MAX_OBJECTS=65536L to avoid this limit but
483   waste more memory.
484 */
485 #define MNG_MAX_OBJECTS 256
486
487 /*
488   If this not defined, spec is interpreted strictly.  If it is
489   defined, an attempt will be made to recover from some errors,
490   including
491       o global PLTE too short
492 */
493 #undef MNG_LOOSE
494
495 /*
496   Don't try to define PNG_MNG_FEATURES_SUPPORTED here.  Make sure
497   it's defined in libpng/pngconf.h, version 1.0.9 or later.  It won't work
498   with earlier versions of libpng.  From libpng-1.0.3a to libpng-1.0.8,
499   PNG_READ|WRITE_EMPTY_PLTE were used but those have been deprecated in
500   libpng in favor of PNG_MNG_FEATURES_SUPPORTED, so we set them here.
501   PNG_MNG_FEATURES_SUPPORTED is disabled by default in libpng-1.0.9 and
502   will be enabled by default in libpng-1.2.0.
503 */
504 #ifdef PNG_MNG_FEATURES_SUPPORTED
505 #  ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
506 #    define PNG_READ_EMPTY_PLTE_SUPPORTED
507 #  endif
508 #  ifndef PNG_WRITE_EMPTY_PLTE_SUPPORTED
509 #    define PNG_WRITE_EMPTY_PLTE_SUPPORTED
510 #  endif
511 #endif
512
513 /*
514   Maximum valid size_t in PNG/MNG chunks is (2^31)-1
515   This macro is only defined in libpng-1.0.3 and later.
516   Previously it was PNG_MAX_UINT but that was deprecated in libpng-1.2.6
517 */
518 #ifndef PNG_UINT_31_MAX
519 #define PNG_UINT_31_MAX (png_uint_32) 0x7fffffffL
520 #endif
521
522 /*
523   Constant strings for known chunk types.  If you need to add a chunk,
524   add a string holding the name here.   To make the code more
525   portable, we use ASCII numbers like this, not characters.
526 */
527
528 static const png_byte mng_MHDR[5]={ 77,  72,  68,  82, (png_byte) '\0'};
529 static const png_byte mng_BACK[5]={ 66,  65,  67,  75, (png_byte) '\0'};
530 static const png_byte mng_BASI[5]={ 66,  65,  83,  73, (png_byte) '\0'};
531 static const png_byte mng_CLIP[5]={ 67,  76,  73,  80, (png_byte) '\0'};
532 static const png_byte mng_CLON[5]={ 67,  76,  79,  78, (png_byte) '\0'};
533 static const png_byte mng_DEFI[5]={ 68,  69,  70,  73, (png_byte) '\0'};
534 static const png_byte mng_DHDR[5]={ 68,  72,  68,  82, (png_byte) '\0'};
535 static const png_byte mng_DISC[5]={ 68,  73,  83,  67, (png_byte) '\0'};
536 static const png_byte mng_ENDL[5]={ 69,  78,  68,  76, (png_byte) '\0'};
537 static const png_byte mng_FRAM[5]={ 70,  82,  65,  77, (png_byte) '\0'};
538 static const png_byte mng_IEND[5]={ 73,  69,  78,  68, (png_byte) '\0'};
539 static const png_byte mng_IHDR[5]={ 73,  72,  68,  82, (png_byte) '\0'};
540 static const png_byte mng_JHDR[5]={ 74,  72,  68,  82, (png_byte) '\0'};
541 static const png_byte mng_LOOP[5]={ 76,  79,  79,  80, (png_byte) '\0'};
542 static const png_byte mng_MAGN[5]={ 77,  65,  71,  78, (png_byte) '\0'};
543 static const png_byte mng_MEND[5]={ 77,  69,  78,  68, (png_byte) '\0'};
544 static const png_byte mng_MOVE[5]={ 77,  79,  86,  69, (png_byte) '\0'};
545 static const png_byte mng_PAST[5]={ 80,  65,  83,  84, (png_byte) '\0'};
546 static const png_byte mng_PLTE[5]={ 80,  76,  84,  69, (png_byte) '\0'};
547 static const png_byte mng_SAVE[5]={ 83,  65,  86,  69, (png_byte) '\0'};
548 static const png_byte mng_SEEK[5]={ 83,  69,  69,  75, (png_byte) '\0'};
549 static const png_byte mng_SHOW[5]={ 83,  72,  79,  87, (png_byte) '\0'};
550 static const png_byte mng_TERM[5]={ 84,  69,  82,  77, (png_byte) '\0'};
551 static const png_byte mng_bKGD[5]={ 98,  75,  71,  68, (png_byte) '\0'};
552 static const png_byte mng_cHRM[5]={ 99,  72,  82,  77, (png_byte) '\0'};
553 static const png_byte mng_gAMA[5]={103,  65,  77,  65, (png_byte) '\0'};
554 static const png_byte mng_iCCP[5]={105,  67,  67,  80, (png_byte) '\0'};
555 static const png_byte mng_nEED[5]={110,  69,  69,  68, (png_byte) '\0'};
556 static const png_byte mng_pHYg[5]={112,  72,  89, 103, (png_byte) '\0'};
557 static const png_byte mng_vpAg[5]={118, 112,  65, 103, (png_byte) '\0'};
558 static const png_byte mng_pHYs[5]={112,  72,  89, 115, (png_byte) '\0'};
559 static const png_byte mng_sBIT[5]={115,  66,  73,  84, (png_byte) '\0'};
560 static const png_byte mng_sRGB[5]={115,  82,  71,  66, (png_byte) '\0'};
561 static const png_byte mng_tRNS[5]={116,  82,  78,  83, (png_byte) '\0'};
562
563 #if defined(JNG_SUPPORTED)
564 static const png_byte mng_IDAT[5]={ 73,  68,  65,  84, (png_byte) '\0'};
565 static const png_byte mng_JDAT[5]={ 74,  68,  65,  84, (png_byte) '\0'};
566 static const png_byte mng_JDAA[5]={ 74,  68,  65,  65, (png_byte) '\0'};
567 static const png_byte mng_JdAA[5]={ 74, 100,  65,  65, (png_byte) '\0'};
568 static const png_byte mng_JSEP[5]={ 74,  83,  69,  80, (png_byte) '\0'};
569 static const png_byte mng_oFFs[5]={111,  70,  70, 115, (png_byte) '\0'};
570 #endif
571
572 #if 0
573 /* Other known chunks that are not yet supported by ImageMagick: */
574 static const png_byte mng_hIST[5]={104,  73,  83,  84, (png_byte) '\0'};
575 static const png_byte mng_iTXt[5]={105,  84,  88, 116, (png_byte) '\0'};
576 static const png_byte mng_sPLT[5]={115,  80,  76,  84, (png_byte) '\0'};
577 static const png_byte mng_sTER[5]={115,  84,  69,  82, (png_byte) '\0'};
578 static const png_byte mng_tEXt[5]={116,  69,  88, 116, (png_byte) '\0'};
579 static const png_byte mng_tIME[5]={116,  73,  77,  69, (png_byte) '\0'};
580 static const png_byte mng_zTXt[5]={122,  84,  88, 116, (png_byte) '\0'};
581 #endif
582
583 typedef struct _MngBox
584 {
585   long
586     left,
587     right,
588     top,
589     bottom;
590 } MngBox;
591
592 typedef struct _MngPair
593 {
594   volatile long
595     a,
596     b;
597 } MngPair;
598
599 #ifdef MNG_OBJECT_BUFFERS
600 typedef struct _MngBuffer
601 {
602
603   size_t
604     height,
605     width;
606
607   Image
608     *image;
609
610   png_color
611     plte[256];
612
613   int
614     reference_count;
615
616   unsigned char
617     alpha_sample_depth,
618     compression_method,
619     color_type,
620     concrete,
621     filter_method,
622     frozen,
623     image_type,
624     interlace_method,
625     pixel_sample_depth,
626     plte_length,
627     sample_depth,
628     viewable;
629 } MngBuffer;
630 #endif
631
632 typedef struct _MngInfo
633 {
634
635 #ifdef MNG_OBJECT_BUFFERS
636   MngBuffer
637     *ob[MNG_MAX_OBJECTS];
638 #endif
639
640   Image *
641     image;
642
643   RectangleInfo
644     page;
645
646   int
647     adjoin,
648 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
649     bytes_in_read_buffer,
650     found_empty_plte,
651 #endif
652     equal_backgrounds,
653     equal_chrms,
654     equal_gammas,
655 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
656     defined(PNG_MNG_FEATURES_SUPPORTED)
657     equal_palettes,
658 #endif
659     equal_physs,
660     equal_srgbs,
661     framing_mode,
662     have_global_bkgd,
663     have_global_chrm,
664     have_global_gama,
665     have_global_phys,
666     have_global_sbit,
667     have_global_srgb,
668     have_saved_bkgd_index,
669     have_write_global_chrm,
670     have_write_global_gama,
671     have_write_global_plte,
672     have_write_global_srgb,
673     need_fram,
674     object_id,
675     old_framing_mode,
676     saved_bkgd_index;
677
678   int
679     new_number_colors;
680
681   ssize_t
682     image_found,
683     loop_count[256],
684     loop_iteration[256],
685     scenes_found,
686     x_off[MNG_MAX_OBJECTS],
687     y_off[MNG_MAX_OBJECTS];
688
689   MngBox
690     clip,
691     frame,
692     image_box,
693     object_clip[MNG_MAX_OBJECTS];
694
695   unsigned char
696     /* These flags could be combined into one byte */
697     exists[MNG_MAX_OBJECTS],
698     frozen[MNG_MAX_OBJECTS],
699     loop_active[256],
700     invisible[MNG_MAX_OBJECTS],
701     viewable[MNG_MAX_OBJECTS];
702
703   MagickOffsetType
704     loop_jump[256];
705
706   png_colorp
707     global_plte;
708
709   png_color_8
710     global_sbit;
711
712   png_byte
713 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
714     read_buffer[8],
715 #endif
716     global_trns[256];
717
718   float
719     global_gamma;
720
721   ChromaticityInfo
722     global_chrm;
723
724   RenderingIntent
725     global_srgb_intent;
726
727   unsigned int
728     delay,
729     global_plte_length,
730     global_trns_length,
731     global_x_pixels_per_unit,
732     global_y_pixels_per_unit,
733     mng_width,
734     mng_height,
735     ticks_per_second;
736
737   MagickBooleanType
738     need_blob;
739
740   unsigned int
741     IsPalette,
742     global_phys_unit_type,
743     basi_warning,
744     clon_warning,
745     dhdr_warning,
746     jhdr_warning,
747     magn_warning,
748     past_warning,
749     phyg_warning,
750     phys_warning,
751     sbit_warning,
752     show_warning,
753     mng_type,
754     write_mng,
755     write_png_colortype,
756     write_png_depth,
757     write_png_compression_level,
758     write_png_compression_strategy,
759     write_png_compression_filter,
760     write_png8,
761     write_png24,
762     write_png32,
763     write_png48,
764     write_png64;
765
766 #ifdef MNG_BASI_SUPPORTED
767   size_t
768     basi_width,
769     basi_height;
770
771   unsigned int
772     basi_depth,
773     basi_color_type,
774     basi_compression_method,
775     basi_filter_type,
776     basi_interlace_method,
777     basi_red,
778     basi_green,
779     basi_blue,
780     basi_alpha,
781     basi_viewable;
782 #endif
783
784   png_uint_16
785     magn_first,
786     magn_last,
787     magn_mb,
788     magn_ml,
789     magn_mr,
790     magn_mt,
791     magn_mx,
792     magn_my,
793     magn_methx,
794     magn_methy;
795
796   PixelInfo
797     mng_global_bkgd;
798
799   /* Added at version 6.6.6-7 */
800   MagickBooleanType
801     ping_exclude_bKGD,
802     ping_exclude_cHRM,
803     ping_exclude_date,
804     ping_exclude_EXIF,
805     ping_exclude_gAMA,
806     ping_exclude_iCCP,
807     /* ping_exclude_iTXt, */
808     ping_exclude_oFFs,
809     ping_exclude_pHYs,
810     ping_exclude_sRGB,
811     ping_exclude_tEXt,
812     ping_exclude_tRNS,
813     ping_exclude_vpAg,
814     ping_exclude_zCCP, /* hex-encoded iCCP */
815     ping_exclude_zTXt,
816     ping_preserve_colormap,
817   /* Added at version 6.8.5-7 */
818     ping_preserve_iCCP,
819   /* Added at version 6.8.9-9 */
820     ping_exclude_tIME;
821
822 } MngInfo;
823 #endif /* VER */
824 \f
825 /*
826   Forward declarations.
827 */
828 static MagickBooleanType
829   WritePNGImage(const ImageInfo *,Image *,ExceptionInfo *);
830
831 static MagickBooleanType
832   WriteMNGImage(const ImageInfo *,Image *,ExceptionInfo *);
833
834 #if defined(JNG_SUPPORTED)
835 static MagickBooleanType
836   WriteJNGImage(const ImageInfo *,Image *,ExceptionInfo *);
837 #endif
838
839 #if PNG_LIBPNG_VER > 10011
840
841
842 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
843 static MagickBooleanType
844 LosslessReduceDepthOK(Image *image,ExceptionInfo *exception)
845 {
846     /* Reduce bit depth if it can be reduced losslessly from 16+ to 8.
847      *
848      * This is true if the high byte and the next highest byte of
849      * each sample of the image, the colormap, and the background color
850      * are equal to each other.  We check this by seeing if the samples
851      * are unchanged when we scale them down to 8 and back up to Quantum.
852      *
853      * We don't use the method GetImageDepth() because it doesn't check
854      * background and doesn't handle PseudoClass specially.
855      */
856
857 #define QuantumToCharToQuantumEqQuantum(quantum) \
858   ((ScaleCharToQuantum((unsigned char) ScaleQuantumToChar(quantum))) == quantum)
859
860     MagickBooleanType
861       ok_to_reduce=MagickFalse;
862
863     if (image->depth >= 16)
864       {
865
866         const Quantum
867           *p;
868
869         ok_to_reduce=
870            QuantumToCharToQuantumEqQuantum(image->background_color.red) &&
871            QuantumToCharToQuantumEqQuantum(image->background_color.green) &&
872            QuantumToCharToQuantumEqQuantum(image->background_color.blue) ?
873            MagickTrue : MagickFalse;
874
875         if (ok_to_reduce != MagickFalse && image->storage_class == PseudoClass)
876           {
877             int indx;
878
879             for (indx=0; indx < (ssize_t) image->colors; indx++)
880               {
881                 ok_to_reduce=(
882                    QuantumToCharToQuantumEqQuantum(
883                    image->colormap[indx].red) &&
884                    QuantumToCharToQuantumEqQuantum(
885                    image->colormap[indx].green) &&
886                    QuantumToCharToQuantumEqQuantum(
887                    image->colormap[indx].blue)) ?
888                    MagickTrue : MagickFalse;
889
890                 if (ok_to_reduce == MagickFalse)
891                    break;
892               }
893           }
894
895         if ((ok_to_reduce != MagickFalse) &&
896             (image->storage_class != PseudoClass))
897           {
898             ssize_t
899               y;
900
901             register ssize_t
902               x;
903
904             for (y=0; y < (ssize_t) image->rows; y++)
905             {
906               p=GetVirtualPixels(image,0,y,image->columns,1,exception);
907
908               if (p == (const Quantum *) NULL)
909                 {
910                   ok_to_reduce = MagickFalse;
911                   break;
912                 }
913
914               for (x=(ssize_t) image->columns-1; x >= 0; x--)
915               {
916                 ok_to_reduce=
917                    QuantumToCharToQuantumEqQuantum(GetPixelRed(image,p)) &&
918                    QuantumToCharToQuantumEqQuantum(GetPixelGreen(image,p)) &&
919                    QuantumToCharToQuantumEqQuantum(GetPixelBlue(image,p)) ?
920                    MagickTrue : MagickFalse;
921
922                 if (ok_to_reduce == MagickFalse)
923                   break;
924
925                 p+=GetPixelChannels(image);
926               }
927               if (x >= 0)
928                 break;
929             }
930           }
931
932         if (ok_to_reduce != MagickFalse)
933           {
934             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
935                 "    OK to reduce PNG bit depth to 8 without loss of info");
936           }
937         else
938           {
939             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
940                 "    Not OK to reduce PNG bit depth to 8 without loss of info");
941           }
942       }
943
944     return ok_to_reduce;
945 }
946 #endif /* MAGICKCORE_QUANTUM_DEPTH >= 16 */
947
948 static const char* PngColorTypeToString(const unsigned int color_type)
949 {
950   const char
951     *result = "Unknown";
952
953   switch (color_type)
954     {
955     case PNG_COLOR_TYPE_GRAY:
956       result = "Gray";
957       break;
958     case PNG_COLOR_TYPE_GRAY_ALPHA:
959       result = "Gray+Alpha";
960       break;
961     case PNG_COLOR_TYPE_PALETTE:
962       result = "Palette";
963       break;
964     case PNG_COLOR_TYPE_RGB:
965       result = "RGB";
966       break;
967     case PNG_COLOR_TYPE_RGB_ALPHA:
968       result = "RGB+Alpha";
969       break;
970     }
971
972   return result;
973 }
974
975 static int
976 Magick_RenderingIntent_to_PNG_RenderingIntent(const RenderingIntent intent)
977 {
978   switch (intent)
979   {
980     case PerceptualIntent:
981        return 0;
982
983     case RelativeIntent:
984        return 1;
985
986     case SaturationIntent:
987        return 2;
988
989     case AbsoluteIntent:
990        return 3;
991
992     default:
993        return -1;
994   }
995 }
996
997 static RenderingIntent
998 Magick_RenderingIntent_from_PNG_RenderingIntent(const int ping_intent)
999 {
1000   switch (ping_intent)
1001   {
1002     case 0:
1003       return PerceptualIntent;
1004
1005     case 1:
1006       return RelativeIntent;
1007
1008     case 2:
1009       return SaturationIntent;
1010
1011     case 3:
1012       return AbsoluteIntent;
1013
1014     default:
1015       return UndefinedIntent;
1016     }
1017 }
1018
1019 static const char *
1020 Magick_RenderingIntentString_from_PNG_RenderingIntent(const int ping_intent)
1021 {
1022   switch (ping_intent)
1023   {
1024     case 0:
1025       return "Perceptual Intent";
1026
1027     case 1:
1028       return "Relative Intent";
1029
1030     case 2:
1031       return "Saturation Intent";
1032
1033     case 3:
1034       return "Absolute Intent";
1035
1036     default:
1037       return "Undefined Intent";
1038     }
1039 }
1040
1041 static const char *
1042 Magick_ColorType_from_PNG_ColorType(const int ping_colortype)
1043 {
1044   switch (ping_colortype)
1045   {
1046     case 0:
1047       return "Grayscale";
1048
1049     case 2:
1050       return "Truecolor";
1051
1052     case 3:
1053       return "Indexed";
1054
1055     case 4:
1056       return "GrayAlpha";
1057
1058     case 6:
1059       return "RGBA";
1060
1061     default:
1062       return "UndefinedColorType";
1063     }
1064 }
1065
1066 #endif /* PNG_LIBPNG_VER > 10011 */
1067 #endif /* MAGICKCORE_PNG_DELEGATE */
1068 \f
1069 /*
1070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1071 %                                                                             %
1072 %                                                                             %
1073 %                                                                             %
1074 %   I s M N G                                                                 %
1075 %                                                                             %
1076 %                                                                             %
1077 %                                                                             %
1078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1079 %
1080 %  IsMNG() returns MagickTrue if the image format type, identified by the
1081 %  magick string, is MNG.
1082 %
1083 %  The format of the IsMNG method is:
1084 %
1085 %      MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1086 %
1087 %  A description of each parameter follows:
1088 %
1089 %    o magick: compare image format pattern against these bytes.
1090 %
1091 %    o length: Specifies the length of the magick string.
1092 %
1093 %
1094 */
1095 static MagickBooleanType IsMNG(const unsigned char *magick,const size_t length)
1096 {
1097   if (length < 8)
1098     return(MagickFalse);
1099
1100   if (memcmp(magick,"\212MNG\r\n\032\n",8) == 0)
1101     return(MagickTrue);
1102
1103   return(MagickFalse);
1104 }
1105 \f
1106 /*
1107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108 %                                                                             %
1109 %                                                                             %
1110 %                                                                             %
1111 %   I s J N G                                                                 %
1112 %                                                                             %
1113 %                                                                             %
1114 %                                                                             %
1115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1116 %
1117 %  IsJNG() returns MagickTrue if the image format type, identified by the
1118 %  magick string, is JNG.
1119 %
1120 %  The format of the IsJNG method is:
1121 %
1122 %      MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1123 %
1124 %  A description of each parameter follows:
1125 %
1126 %    o magick: compare image format pattern against these bytes.
1127 %
1128 %    o length: Specifies the length of the magick string.
1129 %
1130 %
1131 */
1132 static MagickBooleanType IsJNG(const unsigned char *magick,const size_t length)
1133 {
1134   if (length < 8)
1135     return(MagickFalse);
1136
1137   if (memcmp(magick,"\213JNG\r\n\032\n",8) == 0)
1138     return(MagickTrue);
1139
1140   return(MagickFalse);
1141 }
1142 \f
1143 /*
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1145 %                                                                             %
1146 %                                                                             %
1147 %                                                                             %
1148 %   I s P N G                                                                 %
1149 %                                                                             %
1150 %                                                                             %
1151 %                                                                             %
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1153 %
1154 %  IsPNG() returns MagickTrue if the image format type, identified by the
1155 %  magick string, is PNG.
1156 %
1157 %  The format of the IsPNG method is:
1158 %
1159 %      MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1160 %
1161 %  A description of each parameter follows:
1162 %
1163 %    o magick: compare image format pattern against these bytes.
1164 %
1165 %    o length: Specifies the length of the magick string.
1166 %
1167 */
1168 static MagickBooleanType IsPNG(const unsigned char *magick,const size_t length)
1169 {
1170   if (length < 8)
1171     return(MagickFalse);
1172
1173   if (memcmp(magick,"\211PNG\r\n\032\n",8) == 0)
1174     return(MagickTrue);
1175
1176   return(MagickFalse);
1177 }
1178 \f
1179 #if defined(MAGICKCORE_PNG_DELEGATE)
1180 #if defined(__cplusplus) || defined(c_plusplus)
1181 extern "C" {
1182 #endif
1183
1184 #if (PNG_LIBPNG_VER > 10011)
1185 static size_t WriteBlobMSBULong(Image *image,const size_t value)
1186 {
1187   unsigned char
1188     buffer[4];
1189
1190   assert(image != (Image *) NULL);
1191   assert(image->signature == MagickCoreSignature);
1192   buffer[0]=(unsigned char) (value >> 24);
1193   buffer[1]=(unsigned char) (value >> 16);
1194   buffer[2]=(unsigned char) (value >> 8);
1195   buffer[3]=(unsigned char) value;
1196   return((size_t) WriteBlob(image,4,buffer));
1197 }
1198
1199 static void PNGLong(png_bytep p,png_uint_32 value)
1200 {
1201   *p++=(png_byte) ((value >> 24) & 0xff);
1202   *p++=(png_byte) ((value >> 16) & 0xff);
1203   *p++=(png_byte) ((value >> 8) & 0xff);
1204   *p++=(png_byte) (value & 0xff);
1205 }
1206
1207 #if defined(JNG_SUPPORTED)
1208 static void PNGsLong(png_bytep p,png_int_32 value)
1209 {
1210   *p++=(png_byte) ((value >> 24) & 0xff);
1211   *p++=(png_byte) ((value >> 16) & 0xff);
1212   *p++=(png_byte) ((value >> 8) & 0xff);
1213   *p++=(png_byte) (value & 0xff);
1214 }
1215 #endif
1216
1217 static void PNGShort(png_bytep p,png_uint_16 value)
1218 {
1219   *p++=(png_byte) ((value >> 8) & 0xff);
1220   *p++=(png_byte) (value & 0xff);
1221 }
1222
1223 static void PNGType(png_bytep p,const png_byte *type)
1224 {
1225   (void) CopyMagickMemory(p,type,4*sizeof(png_byte));
1226 }
1227
1228 static void LogPNGChunk(MagickBooleanType logging, const png_byte *type,
1229    size_t length)
1230 {
1231   if (logging != MagickFalse)
1232     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1233       "  Writing %c%c%c%c chunk, length: %.20g",
1234       type[0],type[1],type[2],type[3],(double) length);
1235 }
1236 #endif /* PNG_LIBPNG_VER > 10011 */
1237
1238 #if defined(__cplusplus) || defined(c_plusplus)
1239 }
1240 #endif
1241
1242 #if PNG_LIBPNG_VER > 10011
1243 /*
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245 %                                                                             %
1246 %                                                                             %
1247 %                                                                             %
1248 %   R e a d P N G I m a g e                                                   %
1249 %                                                                             %
1250 %                                                                             %
1251 %                                                                             %
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253 %
1254 %  ReadPNGImage() reads a Portable Network Graphics (PNG) or
1255 %  Multiple-image Network Graphics (MNG) image file and returns it.  It
1256 %  allocates the memory necessary for the new Image structure and returns a
1257 %  pointer to the new image or set of images.
1258 %
1259 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
1260 %
1261 %  The format of the ReadPNGImage method is:
1262 %
1263 %      Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
1264 %
1265 %  A description of each parameter follows:
1266 %
1267 %    o image_info: the image info.
1268 %
1269 %    o exception: return any errors or warnings in this structure.
1270 %
1271 %  To do, more or less in chronological order (as of version 5.5.2,
1272 %   November 26, 2002 -- glennrp -- see also "To do" under WriteMNGImage):
1273 %
1274 %    Get 16-bit cheap transparency working.
1275 %
1276 %    (At this point, PNG decoding is supposed to be in full MNG-LC compliance)
1277 %
1278 %    Preserve all unknown and not-yet-handled known chunks found in input
1279 %    PNG file and copy them into output PNG files according to the PNG
1280 %    copying rules.
1281 %
1282 %    (At this point, PNG encoding should be in full MNG compliance)
1283 %
1284 %    Provide options for choice of background to use when the MNG BACK
1285 %    chunk is not present or is not mandatory (i.e., leave transparent,
1286 %    user specified, MNG BACK, PNG bKGD)
1287 %
1288 %    Implement LOOP/ENDL [done, but could do discretionary loops more
1289 %    efficiently by linking in the duplicate frames.].
1290 %
1291 %    Decode and act on the MHDR simplicity profile (offer option to reject
1292 %    files or attempt to process them anyway when the profile isn't LC or VLC).
1293 %
1294 %    Upgrade to full MNG without Delta-PNG.
1295 %
1296 %        o  BACK [done a while ago except for background image ID]
1297 %        o  MOVE [done 15 May 1999]
1298 %        o  CLIP [done 15 May 1999]
1299 %        o  DISC [done 19 May 1999]
1300 %        o  SAVE [partially done 19 May 1999 (marks objects frozen)]
1301 %        o  SEEK [partially done 19 May 1999 (discard function only)]
1302 %        o  SHOW
1303 %        o  PAST
1304 %        o  BASI
1305 %        o  MNG-level tEXt/iTXt/zTXt
1306 %        o  pHYg
1307 %        o  pHYs
1308 %        o  sBIT
1309 %        o  bKGD
1310 %        o  iTXt (wait for libpng implementation).
1311 %
1312 %    Use the scene signature to discover when an identical scene is
1313 %    being reused, and just point to the original image->exception instead
1314 %    of storing another set of pixels.  This not specific to MNG
1315 %    but could be applied generally.
1316 %
1317 %    Upgrade to full MNG with Delta-PNG.
1318 %
1319 %    JNG tEXt/iTXt/zTXt
1320 %
1321 %    We will not attempt to read files containing the CgBI chunk.
1322 %    They are really Xcode files meant for display on the iPhone.
1323 %    These are not valid PNG files and it is impossible to recover
1324 %    the original PNG from files that have been converted to Xcode-PNG,
1325 %    since irretrievable loss of color data has occurred due to the
1326 %    use of premultiplied alpha.
1327 */
1328
1329 #if defined(__cplusplus) || defined(c_plusplus)
1330 extern "C" {
1331 #endif
1332
1333 /*
1334   This the function that does the actual reading of data.  It is
1335   the same as the one supplied in libpng, except that it receives the
1336   datastream from the ReadBlob() function instead of standard input.
1337 */
1338 static void png_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1339 {
1340   Image
1341     *image;
1342
1343   image=(Image *) png_get_io_ptr(png_ptr);
1344   if (length != 0)
1345     {
1346       png_size_t
1347         check;
1348
1349       check=(png_size_t) ReadBlob(image,(size_t) length,data);
1350       if (check != length)
1351         {
1352           char
1353             msg[MagickPathExtent];
1354
1355           (void) FormatLocaleString(msg,MagickPathExtent,
1356             "Expected %.20g bytes; found %.20g bytes",(double) length,
1357             (double) check);
1358           png_warning(png_ptr,msg);
1359           png_error(png_ptr,"Read Exception");
1360         }
1361     }
1362 }
1363
1364 #if !defined(PNG_READ_EMPTY_PLTE_SUPPORTED) && \
1365     !defined(PNG_MNG_FEATURES_SUPPORTED)
1366 /* We use mng_get_data() instead of png_get_data() if we have a libpng
1367  * older than libpng-1.0.3a, which was the first to allow the empty
1368  * PLTE, or a newer libpng in which PNG_MNG_FEATURES_SUPPORTED was
1369  * ifdef'ed out.  Earlier versions would crash if the bKGD chunk was
1370  * encountered after an empty PLTE, so we have to look ahead for bKGD
1371  * chunks and remove them from the datastream that is passed to libpng,
1372  * and store their contents for later use.
1373  */
1374 static void mng_get_data(png_structp png_ptr,png_bytep data,png_size_t length)
1375 {
1376   MngInfo
1377     *mng_info;
1378
1379   Image
1380     *image;
1381
1382   png_size_t
1383     check;
1384
1385   register ssize_t
1386     i;
1387
1388   i=0;
1389   mng_info=(MngInfo *) png_get_io_ptr(png_ptr);
1390   image=(Image *) mng_info->image;
1391   while (mng_info->bytes_in_read_buffer && length)
1392   {
1393     data[i]=mng_info->read_buffer[i];
1394     mng_info->bytes_in_read_buffer--;
1395     length--;
1396     i++;
1397   }
1398   if (length != 0)
1399     {
1400       check=(png_size_t) ReadBlob(image,(size_t) length,(char *) data);
1401
1402       if (check != length)
1403         png_error(png_ptr,"Read Exception");
1404
1405       if (length == 4)
1406         {
1407           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1408               (data[3] == 0))
1409             {
1410               check=(png_size_t) ReadBlob(image,(size_t) length,
1411                 (char *) mng_info->read_buffer);
1412               mng_info->read_buffer[4]=0;
1413               mng_info->bytes_in_read_buffer=4;
1414               if (memcmp(mng_info->read_buffer,mng_PLTE,4) == 0)
1415                 mng_info->found_empty_plte=MagickTrue;
1416               if (memcmp(mng_info->read_buffer,mng_IEND,4) == 0)
1417                 {
1418                   mng_info->found_empty_plte=MagickFalse;
1419                   mng_info->have_saved_bkgd_index=MagickFalse;
1420                 }
1421             }
1422
1423           if ((data[0] == 0) && (data[1] == 0) && (data[2] == 0) &&
1424               (data[3] == 1))
1425             {
1426               check=(png_size_t) ReadBlob(image,(size_t) length,
1427                 (char *) mng_info->read_buffer);
1428               mng_info->read_buffer[4]=0;
1429               mng_info->bytes_in_read_buffer=4;
1430               if (memcmp(mng_info->read_buffer,mng_bKGD,4) == 0)
1431                 if (mng_info->found_empty_plte)
1432                   {
1433                     /*
1434                       Skip the bKGD data byte and CRC.
1435                     */
1436                     check=(png_size_t)
1437                       ReadBlob(image,5,(char *) mng_info->read_buffer);
1438                     check=(png_size_t) ReadBlob(image,(size_t) length,
1439                       (char *) mng_info->read_buffer);
1440                     mng_info->saved_bkgd_index=mng_info->read_buffer[0];
1441                     mng_info->have_saved_bkgd_index=MagickTrue;
1442                     mng_info->bytes_in_read_buffer=0;
1443                   }
1444             }
1445         }
1446     }
1447 }
1448 #endif
1449
1450 static void png_put_data(png_structp png_ptr,png_bytep data,png_size_t length)
1451 {
1452   Image
1453     *image;
1454
1455   image=(Image *) png_get_io_ptr(png_ptr);
1456   if (length != 0)
1457     {
1458       png_size_t
1459         check;
1460
1461       check=(png_size_t) WriteBlob(image,(size_t) length,data);
1462
1463       if (check != length)
1464         png_error(png_ptr,"WriteBlob Failed");
1465     }
1466 }
1467
1468 static void png_flush_data(png_structp png_ptr)
1469 {
1470   (void) png_ptr;
1471 }
1472
1473 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
1474 static int PalettesAreEqual(Image *a,Image *b)
1475 {
1476   ssize_t
1477     i;
1478
1479   if ((a == (Image *) NULL) || (b == (Image *) NULL))
1480     return((int) MagickFalse);
1481
1482   if (a->storage_class != PseudoClass || b->storage_class != PseudoClass)
1483     return((int) MagickFalse);
1484
1485   if (a->colors != b->colors)
1486     return((int) MagickFalse);
1487
1488   for (i=0; i < (ssize_t) a->colors; i++)
1489   {
1490     if ((a->colormap[i].red != b->colormap[i].red) ||
1491         (a->colormap[i].green != b->colormap[i].green) ||
1492         (a->colormap[i].blue != b->colormap[i].blue))
1493       return((int) MagickFalse);
1494   }
1495
1496   return((int) MagickTrue);
1497 }
1498 #endif
1499
1500 static void MngInfoDiscardObject(MngInfo *mng_info,int i)
1501 {
1502   if (i && (i < MNG_MAX_OBJECTS) && (mng_info != (MngInfo *) NULL) &&
1503       mng_info->exists[i] && !mng_info->frozen[i])
1504     {
1505 #ifdef MNG_OBJECT_BUFFERS
1506       if (mng_info->ob[i] != (MngBuffer *) NULL)
1507         {
1508           if (mng_info->ob[i]->reference_count > 0)
1509             mng_info->ob[i]->reference_count--;
1510
1511           if (mng_info->ob[i]->reference_count == 0)
1512             {
1513               if (mng_info->ob[i]->image != (Image *) NULL)
1514                 mng_info->ob[i]->image=DestroyImage(mng_info->ob[i]->image);
1515
1516               mng_info->ob[i]=DestroyString(mng_info->ob[i]);
1517             }
1518         }
1519       mng_info->ob[i]=(MngBuffer *) NULL;
1520 #endif
1521       mng_info->exists[i]=MagickFalse;
1522       mng_info->invisible[i]=MagickFalse;
1523       mng_info->viewable[i]=MagickFalse;
1524       mng_info->frozen[i]=MagickFalse;
1525       mng_info->x_off[i]=0;
1526       mng_info->y_off[i]=0;
1527       mng_info->object_clip[i].left=0;
1528       mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
1529       mng_info->object_clip[i].top=0;
1530       mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
1531     }
1532 }
1533
1534 static void MngInfoFreeStruct(MngInfo *mng_info,
1535     MagickBooleanType *have_mng_structure)
1536 {
1537   if (*have_mng_structure != MagickFalse && (mng_info != (MngInfo *) NULL))
1538     {
1539       register ssize_t
1540         i;
1541
1542       for (i=1; i < MNG_MAX_OBJECTS; i++)
1543         MngInfoDiscardObject(mng_info,i);
1544
1545       if (mng_info->global_plte != (png_colorp) NULL)
1546         mng_info->global_plte=(png_colorp)
1547           RelinquishMagickMemory(mng_info->global_plte);
1548
1549       mng_info=(MngInfo *) RelinquishMagickMemory(mng_info);
1550       *have_mng_structure=MagickFalse;
1551     }
1552 }
1553
1554 static MngBox mng_minimum_box(MngBox box1,MngBox box2)
1555 {
1556   MngBox
1557     box;
1558
1559   box=box1;
1560   if (box.left < box2.left)
1561     box.left=box2.left;
1562
1563   if (box.top < box2.top)
1564     box.top=box2.top;
1565
1566   if (box.right > box2.right)
1567     box.right=box2.right;
1568
1569   if (box.bottom > box2.bottom)
1570     box.bottom=box2.bottom;
1571
1572   return box;
1573 }
1574
1575 static MngBox mng_read_box(MngBox previous_box,char delta_type,unsigned char *p)
1576 {
1577    MngBox
1578       box;
1579
1580   /*
1581     Read clipping boundaries from DEFI, CLIP, FRAM, or PAST chunk.
1582   */
1583   box.left=(ssize_t) ((p[0]  << 24) | (p[1]  << 16) | (p[2]  << 8) | p[3]);
1584   box.right=(ssize_t) ((p[4]  << 24) | (p[5]  << 16) | (p[6]  << 8) | p[7]);
1585   box.top=(ssize_t) ((p[8]  << 24) | (p[9]  << 16) | (p[10] << 8) | p[11]);
1586   box.bottom=(ssize_t) ((p[12] << 24) | (p[13] << 16) | (p[14] << 8) | p[15]);
1587   if (delta_type != 0)
1588     {
1589       box.left+=previous_box.left;
1590       box.right+=previous_box.right;
1591       box.top+=previous_box.top;
1592       box.bottom+=previous_box.bottom;
1593     }
1594
1595   return(box);
1596 }
1597
1598 static MngPair mng_read_pair(MngPair previous_pair,int delta_type,
1599   unsigned char *p)
1600 {
1601   MngPair
1602     pair;
1603   /*
1604     Read two ssize_ts from CLON, MOVE or PAST chunk
1605   */
1606   pair.a=(long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
1607   pair.b=(long) ((p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7]);
1608
1609   if (delta_type != 0)
1610     {
1611       pair.a+=previous_pair.a;
1612       pair.b+=previous_pair.b;
1613     }
1614
1615   return(pair);
1616 }
1617
1618 static long mng_get_long(unsigned char *p)
1619 {
1620   return((long) ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]));
1621 }
1622
1623 typedef struct _PNGErrorInfo
1624 {
1625   Image
1626     *image;
1627
1628   ExceptionInfo
1629     *exception;
1630 } PNGErrorInfo;
1631
1632 static void MagickPNGErrorHandler(png_struct *ping,png_const_charp message)
1633 {
1634   ExceptionInfo
1635     *exception;
1636
1637   Image
1638     *image;
1639
1640   PNGErrorInfo
1641     *error_info;
1642
1643   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1644   image=error_info->image;
1645   exception=error_info->exception;
1646
1647   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1648     "  libpng-%s error: %s", png_get_libpng_ver(NULL),message);
1649
1650   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,message,
1651     "`%s'",image->filename);
1652
1653 #if (PNG_LIBPNG_VER < 10500)
1654   /* A warning about deprecated use of jmpbuf here is unavoidable if you
1655    * are building with libpng-1.4.x and can be ignored.
1656    */
1657   longjmp(ping->jmpbuf,1);
1658 #else
1659   png_longjmp(ping,1);
1660 #endif
1661 }
1662
1663 static void MagickPNGWarningHandler(png_struct *ping,png_const_charp message)
1664 {
1665   ExceptionInfo
1666     *exception;
1667
1668   Image
1669     *image;
1670
1671   PNGErrorInfo
1672     *error_info;
1673
1674   if (LocaleCompare(message, "Missing PLTE before tRNS") == 0)
1675     png_error(ping, message);
1676
1677   error_info=(PNGErrorInfo *) png_get_error_ptr(ping);
1678   image=error_info->image;
1679   exception=error_info->exception;
1680   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1681     "  libpng-%s warning: %s", png_get_libpng_ver(NULL),message);
1682
1683   (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
1684     message,"`%s'",image->filename);
1685 }
1686
1687 #ifdef PNG_USER_MEM_SUPPORTED
1688 #if PNG_LIBPNG_VER >= 10400
1689 static png_voidp Magick_png_malloc(png_structp png_ptr,png_alloc_size_t size)
1690 #else
1691 static png_voidp Magick_png_malloc(png_structp png_ptr,png_size_t size)
1692 #endif
1693 {
1694   (void) png_ptr;
1695   return((png_voidp) AcquireMagickMemory((size_t) size));
1696 }
1697
1698 /*
1699   Free a pointer.  It is removed from the list at the same time.
1700 */
1701 static png_free_ptr Magick_png_free(png_structp png_ptr,png_voidp ptr)
1702 {
1703   (void) png_ptr;
1704   ptr=RelinquishMagickMemory(ptr);
1705   return((png_free_ptr) NULL);
1706 }
1707 #endif
1708
1709 #if defined(__cplusplus) || defined(c_plusplus)
1710 }
1711 #endif
1712
1713 static int
1714 Magick_png_read_raw_profile(png_struct *ping,Image *image,
1715    const ImageInfo *image_info, png_textp text,int ii,ExceptionInfo *exception)
1716 {
1717   register ssize_t
1718     i;
1719
1720   register unsigned char
1721     *dp;
1722
1723   register png_charp
1724     sp;
1725
1726   png_uint_32
1727     length,
1728     nibbles;
1729
1730   StringInfo
1731     *profile;
1732
1733   const unsigned char
1734     unhex[103]={0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1735                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1736                  0,0,0,0,0,0,0,0,0,1, 2,3,4,5,6,7,8,9,0,0,
1737                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,
1738                  0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,10,11,12,
1739                  13,14,15};
1740
1741   sp=text[ii].text+1;
1742   /* look for newline */
1743   while (*sp != '\n')
1744      sp++;
1745
1746   /* look for length */
1747   while (*sp == '\0' || *sp == ' ' || *sp == '\n')
1748      sp++;
1749
1750   length=(png_uint_32) StringToLong(sp);
1751
1752   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1753        "      length: %lu",(unsigned long) length);
1754
1755   while (*sp != ' ' && *sp != '\n')
1756      sp++;
1757
1758   /* allocate space */
1759   if (length == 0)
1760   {
1761     png_warning(ping,"invalid profile length");
1762     return(MagickFalse);
1763   }
1764
1765   profile=BlobToStringInfo((const void *) NULL,length);
1766
1767   if (profile == (StringInfo *) NULL)
1768   {
1769     png_warning(ping, "unable to copy profile");
1770     return(MagickFalse);
1771   }
1772
1773   /* copy profile, skipping white space and column 1 "=" signs */
1774   dp=GetStringInfoDatum(profile);
1775   nibbles=length*2;
1776
1777   for (i=0; i < (ssize_t) nibbles; i++)
1778   {
1779     while (*sp < '0' || (*sp > '9' && *sp < 'a') || *sp > 'f')
1780     {
1781       if (*sp == '\0')
1782         {
1783           png_warning(ping, "ran out of profile data");
1784           profile=DestroyStringInfo(profile);
1785           return(MagickFalse);
1786         }
1787       sp++;
1788     }
1789
1790     if (i%2 == 0)
1791       *dp=(unsigned char) (16*unhex[(int) *sp++]);
1792
1793     else
1794       (*dp++)+=unhex[(int) *sp++];
1795   }
1796   /*
1797     We have already read "Raw profile type.
1798   */
1799   (void) SetImageProfile(image,&text[ii].key[17],profile,exception);
1800   profile=DestroyStringInfo(profile);
1801
1802   if (image_info->verbose)
1803     (void) printf(" Found a generic profile, type %s\n",&text[ii].key[17]);
1804
1805   return MagickTrue;
1806 }
1807
1808 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
1809 static int read_vpag_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
1810 {
1811   Image
1812     *image;
1813
1814
1815   /* The unknown chunk structure contains the chunk data:
1816      png_byte name[5];
1817      png_byte *data;
1818      png_size_t size;
1819
1820      Note that libpng has already taken care of the CRC handling.
1821   */
1822
1823   LogMagickEvent(CoderEvent,GetMagickModule(),
1824      " read_vpag_chunk: found %c%c%c%c chunk",
1825        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
1826
1827   if (chunk->name[0] != 118 || chunk->name[1] != 112 ||
1828       chunk->name[2] != 65 ||chunk-> name[3] != 103)
1829     return(0); /* Did not recognize */
1830
1831   /* recognized vpAg */
1832
1833   if (chunk->size != 9)
1834     return(-1); /* Error return */
1835
1836   if (chunk->data[8] != 0)
1837     return(0);  /* ImageMagick requires pixel units */
1838
1839   image=(Image *) png_get_user_chunk_ptr(ping);
1840
1841   image->page.width=(size_t) ((chunk->data[0] << 24) |
1842      (chunk->data[1] << 16) | (chunk->data[2] << 8) | chunk->data[3]);
1843
1844   image->page.height=(size_t) ((chunk->data[4] << 24) |
1845      (chunk->data[5] << 16) | (chunk->data[6] << 8) | chunk->data[7]);
1846
1847   /* Return one of the following: */
1848      /* return(-n);  chunk had an error */
1849      /* return(0);  did not recognize */
1850      /* return(n);  success */
1851
1852   return(1);
1853
1854 }
1855 #endif
1856
1857 #if defined(PNG_tIME_SUPPORTED)
1858 static void read_tIME_chunk(Image *image,png_struct *ping,png_info *info,
1859   ExceptionInfo *exception)
1860 {
1861   png_timep
1862     time;
1863
1864   if (png_get_tIME(ping,info,&time))
1865     {
1866       char
1867         timestamp[21];
1868
1869       FormatLocaleString(timestamp,21,"%04d-%02d-%02dT%02d:%02d:%02dZ",
1870         time->year,time->month,time->day,time->hour,time->minute,time->second);
1871       SetImageProperty(image,"png:tIME",timestamp,exception);
1872     }
1873 }
1874 #endif
1875
1876 /*
1877 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1878 %                                                                             %
1879 %                                                                             %
1880 %                                                                             %
1881 %   R e a d O n e P N G I m a g e                                             %
1882 %                                                                             %
1883 %                                                                             %
1884 %                                                                             %
1885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1886 %
1887 %  ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file
1888 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
1889 %  necessary for the new Image structure and returns a pointer to the new
1890 %  image.
1891 %
1892 %  The format of the ReadOnePNGImage method is:
1893 %
1894 %      Image *ReadOnePNGImage(MngInfo *mng_info, const ImageInfo *image_info,
1895 %         ExceptionInfo *exception)
1896 %
1897 %  A description of each parameter follows:
1898 %
1899 %    o mng_info: Specifies a pointer to a MngInfo structure.
1900 %
1901 %    o image_info: the image info.
1902 %
1903 %    o exception: return any errors or warnings in this structure.
1904 %
1905 */
1906 static Image *ReadOnePNGImage(MngInfo *mng_info,
1907     const ImageInfo *image_info, ExceptionInfo *exception)
1908 {
1909   /* Read one PNG image */
1910
1911   /* To do: Read the tEXt/Creation Time chunk into the date:create property */
1912
1913   Image
1914     *image;
1915
1916   char
1917     im_vers[32],
1918     libpng_runv[32],
1919     libpng_vers[32],
1920     zlib_runv[32],
1921     zlib_vers[32];
1922
1923   int
1924     intent, /* "PNG Rendering intent", which is ICC intent + 1 */
1925     num_raw_profiles,
1926     num_text,
1927     num_text_total,
1928     num_passes,
1929     number_colors,
1930     pass,
1931     ping_bit_depth,
1932     ping_color_type,
1933     ping_file_depth,
1934     ping_interlace_method,
1935     ping_compression_method,
1936     ping_filter_method,
1937     ping_num_trans,
1938     unit_type;
1939
1940   double
1941     file_gamma;
1942
1943   MagickBooleanType
1944     logging,
1945     ping_found_cHRM,
1946     ping_found_gAMA,
1947     ping_found_iCCP,
1948     ping_found_sRGB,
1949     ping_found_sRGB_cHRM,
1950     ping_preserve_iCCP,
1951     status;
1952
1953   MemoryInfo
1954     *volatile pixel_info;
1955
1956   PixelInfo
1957     transparent_color;
1958
1959   PNGErrorInfo
1960     error_info;
1961
1962   png_bytep
1963      ping_trans_alpha;
1964
1965   png_color_16p
1966      ping_background,
1967      ping_trans_color;
1968
1969   png_info
1970     *end_info,
1971     *ping_info;
1972
1973   png_struct
1974     *ping;
1975
1976   png_textp
1977     text;
1978
1979   png_uint_32
1980     ping_height,
1981     ping_width,
1982     x_resolution,
1983     y_resolution;
1984
1985   QuantumInfo
1986     *quantum_info;
1987
1988   ssize_t
1989     ping_rowbytes,
1990     y;
1991
1992   register unsigned char
1993     *p;
1994
1995   register ssize_t
1996     i,
1997     x;
1998
1999   register Quantum
2000     *q;
2001
2002   size_t
2003     length,
2004     row_offset;
2005
2006   ssize_t
2007     j;
2008
2009   unsigned char
2010     *ping_pixels;
2011
2012 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
2013   png_byte unused_chunks[]=
2014   {
2015     104,  73,  83,  84, (png_byte) '\0',   /* hIST */
2016     105,  84,  88, 116, (png_byte) '\0',   /* iTXt */
2017     112,  67,  65,  76, (png_byte) '\0',   /* pCAL */
2018     115,  67,  65,  76, (png_byte) '\0',   /* sCAL */
2019     115,  80,  76,  84, (png_byte) '\0',   /* sPLT */
2020 #if !defined(PNG_tIME_SUPPORTED)
2021     116,  73,  77,  69, (png_byte) '\0',   /* tIME */
2022 #endif
2023 #ifdef PNG_APNG_SUPPORTED /* libpng was built with APNG patch; */
2024                           /* ignore the APNG chunks */
2025      97,  99,  84,  76, (png_byte) '\0',   /* acTL */
2026     102,  99,  84,  76, (png_byte) '\0',   /* fcTL */
2027     102, 100,  65,  84, (png_byte) '\0',   /* fdAT */
2028 #endif
2029   };
2030 #endif
2031
2032   /* Define these outside of the following "if logging()" block so they will
2033    * show in debuggers.
2034    */
2035   *im_vers='\0';
2036   (void) ConcatenateMagickString(im_vers,
2037          MagickLibVersionText,32);
2038   (void) ConcatenateMagickString(im_vers,
2039          MagickLibAddendum,32);
2040
2041   *libpng_vers='\0';
2042   (void) ConcatenateMagickString(libpng_vers,
2043          PNG_LIBPNG_VER_STRING,32);
2044   *libpng_runv='\0';
2045   (void) ConcatenateMagickString(libpng_runv,
2046          png_get_libpng_ver(NULL),32);
2047
2048   *zlib_vers='\0';
2049   (void) ConcatenateMagickString(zlib_vers,
2050          ZLIB_VERSION,32);
2051   *zlib_runv='\0';
2052   (void) ConcatenateMagickString(zlib_runv,
2053          zlib_version,32);
2054
2055   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
2056        "  Enter ReadOnePNGImage()\n"
2057        "    IM version     = %s\n"
2058        "    Libpng version = %s",
2059        im_vers, libpng_vers);
2060
2061   if (logging != MagickFalse)
2062   {
2063     if (LocaleCompare(libpng_vers,libpng_runv) != 0)
2064     {
2065     LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2066         libpng_runv);
2067     }
2068     LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
2069         zlib_vers);
2070     if (LocaleCompare(zlib_vers,zlib_runv) != 0)
2071     {
2072     LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
2073         zlib_runv);
2074     }
2075   }
2076
2077 #if (PNG_LIBPNG_VER < 10200)
2078   if (image_info->verbose)
2079     printf("Your PNG library (libpng-%s) is rather old.\n",
2080        PNG_LIBPNG_VER_STRING);
2081 #endif
2082
2083 #if (PNG_LIBPNG_VER >= 10400)
2084 #  ifndef  PNG_TRANSFORM_GRAY_TO_RGB    /* Added at libpng-1.4.0beta67 */
2085   if (image_info->verbose)
2086     {
2087       printf("Your PNG library (libpng-%s) is an old beta version.\n",
2088            PNG_LIBPNG_VER_STRING);
2089       printf("Please update it.\n");
2090     }
2091 #  endif
2092 #endif
2093
2094
2095   quantum_info = (QuantumInfo *) NULL;
2096   image=mng_info->image;
2097
2098   if (logging != MagickFalse)
2099   {
2100     (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2101        "    Before reading:\n"
2102        "      image->alpha_trait=%d"
2103        "      image->rendering_intent=%d\n"
2104        "      image->colorspace=%d\n"
2105        "      image->gamma=%f",
2106        (int) image->alpha_trait, (int) image->rendering_intent,
2107        (int) image->colorspace, image->gamma);
2108   }
2109   intent=Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
2110
2111   /* Set to an out-of-range color unless tRNS chunk is present */
2112   transparent_color.red=65537;
2113   transparent_color.green=65537;
2114   transparent_color.blue=65537;
2115   transparent_color.alpha=65537;
2116
2117   number_colors=0;
2118   num_text = 0;
2119   num_text_total = 0;
2120   num_raw_profiles = 0;
2121
2122   ping_found_cHRM = MagickFalse;
2123   ping_found_gAMA = MagickFalse;
2124   ping_found_iCCP = MagickFalse;
2125   ping_found_sRGB = MagickFalse;
2126   ping_found_sRGB_cHRM = MagickFalse;
2127   ping_preserve_iCCP = MagickFalse;
2128
2129
2130   /*
2131     Allocate the PNG structures
2132   */
2133 #ifdef PNG_USER_MEM_SUPPORTED
2134  error_info.image=image;
2135  error_info.exception=exception;
2136  ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
2137    MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,
2138    (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2139 #else
2140   ping=png_create_read_struct(PNG_LIBPNG_VER_STRING,&error_info,
2141     MagickPNGErrorHandler,MagickPNGWarningHandler);
2142 #endif
2143   if (ping == (png_struct *) NULL)
2144     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2145
2146   ping_info=png_create_info_struct(ping);
2147
2148   if (ping_info == (png_info *) NULL)
2149     {
2150       png_destroy_read_struct(&ping,(png_info **) NULL,(png_info **) NULL);
2151       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2152     }
2153
2154   end_info=png_create_info_struct(ping);
2155
2156   if (end_info == (png_info *) NULL)
2157     {
2158       png_destroy_read_struct(&ping,&ping_info,(png_info **) NULL);
2159       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
2160     }
2161
2162   pixel_info=(MemoryInfo *) NULL;
2163
2164   if (setjmp(png_jmpbuf(ping)))
2165     {
2166       /*
2167         PNG image is corrupt.
2168       */
2169       png_destroy_read_struct(&ping,&ping_info,&end_info);
2170
2171 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2172       UnlockSemaphoreInfo(ping_semaphore);
2173 #endif
2174
2175       if (pixel_info != (MemoryInfo *) NULL)
2176         pixel_info=RelinquishVirtualMemory(pixel_info);
2177
2178       if (logging != MagickFalse)
2179         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2180           "  exit ReadOnePNGImage() with error.");
2181
2182       return(GetFirstImageInList(image));
2183     }
2184
2185   /* {  For navigation to end of SETJMP-protected block.  Within this
2186    *    block, use png_error() instead of Throwing an Exception, to ensure
2187    *    that libpng is able to clean up, and that the semaphore is unlocked.
2188    */
2189
2190 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
2191   LockSemaphoreInfo(ping_semaphore);
2192 #endif
2193
2194 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
2195   /* Allow benign errors */
2196   png_set_benign_errors(ping, 1);
2197 #endif
2198
2199 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2200   /* Reject images with too many rows or columns */
2201   png_set_user_limits(ping,
2202     (png_uint_32) MagickMin(0x7fffffffL,
2203         GetMagickResourceLimit(WidthResource)),
2204     (png_uint_32) MagickMin(0x7fffffffL,
2205         GetMagickResourceLimit(HeightResource)));
2206 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
2207
2208   /*
2209     Prepare PNG for reading.
2210   */
2211
2212   mng_info->image_found++;
2213   png_set_sig_bytes(ping,8);
2214
2215   if (LocaleCompare(image_info->magick,"MNG") == 0)
2216     {
2217 #if defined(PNG_MNG_FEATURES_SUPPORTED)
2218       (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
2219       png_set_read_fn(ping,image,png_get_data);
2220 #else
2221 #if defined(PNG_READ_EMPTY_PLTE_SUPPORTED)
2222       png_permit_empty_plte(ping,MagickTrue);
2223       png_set_read_fn(ping,image,png_get_data);
2224 #else
2225       mng_info->image=image;
2226       mng_info->bytes_in_read_buffer=0;
2227       mng_info->found_empty_plte=MagickFalse;
2228       mng_info->have_saved_bkgd_index=MagickFalse;
2229       png_set_read_fn(ping,mng_info,mng_get_data);
2230 #endif
2231 #endif
2232     }
2233
2234   else
2235     png_set_read_fn(ping,image,png_get_data);
2236
2237   {
2238     const char
2239       *value;
2240
2241     value=GetImageOption(image_info,"profile:skip");
2242
2243     if (IsOptionMember("ICC",value) == MagickFalse)
2244     {
2245
2246        value=GetImageOption(image_info,"png:preserve-iCCP");
2247
2248        if (value == NULL)
2249           value=GetImageArtifact(image,"png:preserve-iCCP");
2250
2251        if (value != NULL)
2252           ping_preserve_iCCP=MagickTrue;
2253
2254 #if defined(PNG_SKIP_sRGB_CHECK_PROFILE) && defined(PNG_SET_OPTION_SUPPORTED)
2255        /* Don't let libpng check for ICC/sRGB profile because we're going
2256         * to do that anyway.  This feature was added at libpng-1.6.12.
2257         * If logging, go ahead and check and issue a warning as appropriate.
2258         */
2259        if (logging == MagickFalse)
2260           png_set_option(ping, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
2261 #endif
2262     }
2263 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2264     else
2265     {
2266        png_set_keep_unknown_chunks(ping, 1, mng_iCCP, 1);
2267     }
2268 #endif
2269   }
2270 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
2271   /* Ignore unused chunks and all unknown chunks except for vpAg */
2272 #if PNG_LIBPNG_VER < 10700 /* Avoid libpng16 warning */
2273   png_set_keep_unknown_chunks(ping, 2, NULL, 0);
2274 #else
2275   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
2276 #endif
2277   png_set_keep_unknown_chunks(ping, 2, mng_vpAg, 1);
2278   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
2279      (int)sizeof(unused_chunks)/5);
2280   /* Callback for other unknown chunks */
2281   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
2282 #endif
2283
2284 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
2285 #  if (PNG_LIBPNG_VER >= 10400)
2286     /* Limit the size of the chunk storage cache used for sPLT, text,
2287      * and unknown chunks.
2288      */
2289     png_set_chunk_cache_max(ping, 32767);
2290 #  endif
2291 #endif
2292
2293 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
2294     /* Disable new libpng-1.5.10 feature */
2295     png_set_check_for_invalid_index (ping, 0);
2296 #endif
2297
2298 #if (PNG_LIBPNG_VER < 10400)
2299 #  if defined(PNG_USE_PNGGCCRD) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \
2300    (PNG_LIBPNG_VER >= 10200) && (PNG_LIBPNG_VER < 10220) && defined(__i386__)
2301   /* Disable thread-unsafe features of pnggccrd */
2302   if (png_access_version_number() >= 10200)
2303   {
2304     png_uint_32 mmx_disable_mask=0;
2305     png_uint_32 asm_flags;
2306
2307     mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
2308                         | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
2309                         | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
2310                         | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH );
2311     asm_flags=png_get_asm_flags(ping);
2312     png_set_asm_flags(ping, asm_flags & ~mmx_disable_mask);
2313   }
2314 #  endif
2315 #endif
2316
2317   png_read_info(ping,ping_info);
2318
2319   png_get_IHDR(ping,ping_info,&ping_width,&ping_height,
2320                &ping_bit_depth,&ping_color_type,
2321                &ping_interlace_method,&ping_compression_method,
2322                &ping_filter_method);
2323
2324   ping_file_depth = ping_bit_depth;
2325
2326   /* Swap bytes if requested */
2327   if (ping_file_depth == 16)
2328   {
2329      const char
2330        *value;
2331
2332      value=GetImageOption(image_info,"png:swap-bytes");
2333
2334      if (value == NULL)
2335         value=GetImageArtifact(image,"png:swap-bytes");
2336
2337      if (value != NULL)
2338         png_set_swap(ping);
2339   }
2340
2341   /* Save bit-depth and color-type in case we later want to write a PNG00 */
2342   {
2343       char
2344         msg[MagickPathExtent];
2345
2346       (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_color_type);
2347       (void) SetImageProperty(image,"png:IHDR.color-type-orig",msg,exception);
2348
2349       (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_bit_depth);
2350       (void) SetImageProperty(image,"png:IHDR.bit-depth-orig",msg,exception);
2351   }
2352
2353   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
2354                       &ping_trans_color);
2355
2356   (void) png_get_bKGD(ping, ping_info, &ping_background);
2357
2358   if (ping_bit_depth < 8)
2359     {
2360        png_set_packing(ping);
2361        ping_bit_depth = 8;
2362     }
2363
2364   image->depth=ping_bit_depth;
2365   image->depth=GetImageQuantumDepth(image,MagickFalse);
2366   image->interlace=ping_interlace_method != 0 ? PNGInterlace : NoInterlace;
2367
2368   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2369       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2370     {
2371       image->rendering_intent=UndefinedIntent;
2372       intent=Magick_RenderingIntent_to_PNG_RenderingIntent(UndefinedIntent);
2373       (void) ResetMagickMemory(&image->chromaticity,0,
2374         sizeof(image->chromaticity));
2375     }
2376
2377   if (logging != MagickFalse)
2378     {
2379       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2380         "    PNG width: %.20g, height: %.20g\n"
2381         "    PNG color_type: %d, bit_depth: %d\n"
2382         "    PNG compression_method: %d\n"
2383         "    PNG interlace_method: %d, filter_method: %d",
2384         (double) ping_width, (double) ping_height,
2385         ping_color_type, ping_bit_depth,
2386         ping_compression_method,
2387         ping_interlace_method,ping_filter_method);
2388
2389     }
2390
2391   if (png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2392     {
2393       ping_found_iCCP=MagickTrue;
2394       if (logging != MagickFalse)
2395         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2396           "    Found PNG iCCP chunk.");
2397     }
2398
2399   if (png_get_valid(ping,ping_info,PNG_INFO_gAMA))
2400     {
2401       ping_found_gAMA=MagickTrue;
2402       if (logging != MagickFalse)
2403         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2404           "    Found PNG gAMA chunk.");
2405     }
2406
2407   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2408     {
2409       ping_found_cHRM=MagickTrue;
2410       if (logging != MagickFalse)
2411         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2412           "    Found PNG cHRM chunk.");
2413     }
2414
2415   if (ping_found_iCCP != MagickTrue && png_get_valid(ping,ping_info,
2416       PNG_INFO_sRGB))
2417     {
2418       ping_found_sRGB=MagickTrue;
2419       if (logging != MagickFalse)
2420         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2421           "    Found PNG sRGB chunk.");
2422     }
2423
2424 #ifdef PNG_READ_iCCP_SUPPORTED
2425     if (ping_found_iCCP !=MagickTrue &&
2426       ping_found_sRGB != MagickTrue &&
2427       png_get_valid(ping,ping_info, PNG_INFO_iCCP))
2428     {
2429       ping_found_iCCP=MagickTrue;
2430       if (logging != MagickFalse)
2431         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2432           "    Found PNG iCCP chunk.");
2433     }
2434
2435   if (png_get_valid(ping,ping_info,PNG_INFO_iCCP))
2436     {
2437       int
2438         compression;
2439
2440 #if (PNG_LIBPNG_VER < 10500)
2441       png_charp
2442         info;
2443 #else
2444       png_bytep
2445         info;
2446 #endif
2447
2448       png_charp
2449         name;
2450
2451       png_uint_32
2452         profile_length;
2453
2454       (void) png_get_iCCP(ping,ping_info,&name,(int *) &compression,&info,
2455         &profile_length);
2456
2457       if (profile_length != 0)
2458         {
2459           StringInfo
2460             *profile;
2461
2462           if (logging != MagickFalse)
2463             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2464               "    Reading PNG iCCP chunk.");
2465
2466           profile=BlobToStringInfo(info,profile_length);
2467
2468           if (profile == (StringInfo *) NULL)
2469           {
2470             png_warning(ping, "ICC profile is NULL");
2471             profile=DestroyStringInfo(profile);
2472           }
2473           else
2474           {
2475             if (ping_preserve_iCCP == MagickFalse)
2476             {
2477                  int
2478                    icheck,
2479                    got_crc=0;
2480
2481
2482                  png_uint_32
2483                    length,
2484                    profile_crc=0;
2485
2486                  unsigned char
2487                    *data;
2488
2489                  length=(png_uint_32) GetStringInfoLength(profile);
2490
2491                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
2492                  {
2493                    if (length == sRGB_info[icheck].len)
2494                    {
2495                      if (got_crc == 0)
2496                      {
2497                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2498                          "    Got a %lu-byte ICC profile (potentially sRGB)",
2499                          (unsigned long) length);
2500
2501                        data=GetStringInfoDatum(profile);
2502                        profile_crc=crc32(0,data,length);
2503
2504                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2505                            "      with crc=%8x",(unsigned int) profile_crc);
2506                        got_crc++;
2507                      }
2508
2509                      if (profile_crc == sRGB_info[icheck].crc)
2510                      {
2511                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2512                             "      It is sRGB with rendering intent = %s",
2513                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
2514                              sRGB_info[icheck].intent));
2515                         if (image->rendering_intent==UndefinedIntent)
2516                         {
2517                           image->rendering_intent=
2518                           Magick_RenderingIntent_from_PNG_RenderingIntent(
2519                              sRGB_info[icheck].intent);
2520                         }
2521                         break;
2522                      }
2523                    }
2524                  }
2525                  if (sRGB_info[icheck].len == 0)
2526                  {
2527                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2528                         "    Got a %lu-byte ICC profile not recognized as sRGB",
2529                         (unsigned long) length);
2530                     (void) SetImageProfile(image,"icc",profile,exception);
2531                  }
2532             }
2533             else /* Preserve-iCCP */
2534             {
2535                     (void) SetImageProfile(image,"icc",profile,exception);
2536             }
2537
2538             profile=DestroyStringInfo(profile);
2539           }
2540       }
2541     }
2542 #endif
2543
2544 #if defined(PNG_READ_sRGB_SUPPORTED)
2545   {
2546     if (ping_found_iCCP==MagickFalse && png_get_valid(ping,ping_info,
2547         PNG_INFO_sRGB))
2548     {
2549       if (png_get_sRGB(ping,ping_info,&intent))
2550       {
2551         if (image->rendering_intent == UndefinedIntent)
2552           image->rendering_intent=
2553              Magick_RenderingIntent_from_PNG_RenderingIntent (intent);
2554
2555         if (logging != MagickFalse)
2556           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2557             "    Reading PNG sRGB chunk: rendering_intent: %d",intent);
2558       }
2559     }
2560
2561     else if (mng_info->have_global_srgb)
2562       {
2563         if (image->rendering_intent == UndefinedIntent)
2564           image->rendering_intent=
2565             Magick_RenderingIntent_from_PNG_RenderingIntent
2566             (mng_info->global_srgb_intent);
2567       }
2568   }
2569 #endif
2570
2571
2572   {
2573      if (!png_get_gAMA(ping,ping_info,&file_gamma))
2574        if (mng_info->have_global_gama)
2575          png_set_gAMA(ping,ping_info,mng_info->global_gamma);
2576
2577      if (png_get_gAMA(ping,ping_info,&file_gamma))
2578        {
2579          image->gamma=(float) file_gamma;
2580          if (logging != MagickFalse)
2581            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2582              "    Reading PNG gAMA chunk: gamma: %f",file_gamma);
2583        }
2584   }
2585
2586   if (!png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2587     {
2588       if (mng_info->have_global_chrm != MagickFalse)
2589         {
2590           (void) png_set_cHRM(ping,ping_info,
2591             mng_info->global_chrm.white_point.x,
2592             mng_info->global_chrm.white_point.y,
2593             mng_info->global_chrm.red_primary.x,
2594             mng_info->global_chrm.red_primary.y,
2595             mng_info->global_chrm.green_primary.x,
2596             mng_info->global_chrm.green_primary.y,
2597             mng_info->global_chrm.blue_primary.x,
2598             mng_info->global_chrm.blue_primary.y);
2599         }
2600     }
2601
2602   if (png_get_valid(ping,ping_info,PNG_INFO_cHRM))
2603     {
2604       (void) png_get_cHRM(ping,ping_info,
2605         &image->chromaticity.white_point.x,
2606         &image->chromaticity.white_point.y,
2607         &image->chromaticity.red_primary.x,
2608         &image->chromaticity.red_primary.y,
2609         &image->chromaticity.green_primary.x,
2610         &image->chromaticity.green_primary.y,
2611         &image->chromaticity.blue_primary.x,
2612         &image->chromaticity.blue_primary.y);
2613
2614        ping_found_cHRM=MagickTrue;
2615
2616        if (image->chromaticity.red_primary.x>0.6399f &&
2617            image->chromaticity.red_primary.x<0.6401f &&
2618            image->chromaticity.red_primary.y>0.3299f &&
2619            image->chromaticity.red_primary.y<0.3301f &&
2620            image->chromaticity.green_primary.x>0.2999f &&
2621            image->chromaticity.green_primary.x<0.3001f &&
2622            image->chromaticity.green_primary.y>0.5999f &&
2623            image->chromaticity.green_primary.y<0.6001f &&
2624            image->chromaticity.blue_primary.x>0.1499f &&
2625            image->chromaticity.blue_primary.x<0.1501f &&
2626            image->chromaticity.blue_primary.y>0.0599f &&
2627            image->chromaticity.blue_primary.y<0.0601f &&
2628            image->chromaticity.white_point.x>0.3126f &&
2629            image->chromaticity.white_point.x<0.3128f &&
2630            image->chromaticity.white_point.y>0.3289f &&
2631            image->chromaticity.white_point.y<0.3291f)
2632           ping_found_sRGB_cHRM=MagickTrue;
2633     }
2634
2635   if (image->rendering_intent != UndefinedIntent)
2636     {
2637       if (ping_found_sRGB != MagickTrue &&
2638           (ping_found_gAMA != MagickTrue ||
2639           (image->gamma > .45 && image->gamma < .46)) &&
2640           (ping_found_cHRM != MagickTrue ||
2641           ping_found_sRGB_cHRM != MagickFalse) &&
2642           ping_found_iCCP != MagickTrue)
2643       {
2644          png_set_sRGB(ping,ping_info,
2645             Magick_RenderingIntent_to_PNG_RenderingIntent
2646             (image->rendering_intent));
2647          file_gamma=1.000f/2.200f;
2648          ping_found_sRGB=MagickTrue;
2649          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2650            "    Setting sRGB as if in input");
2651       }
2652     }
2653
2654 #if defined(PNG_oFFs_SUPPORTED)
2655   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
2656     {
2657       image->page.x=(ssize_t) png_get_x_offset_pixels(ping, ping_info);
2658       image->page.y=(ssize_t) png_get_y_offset_pixels(ping, ping_info);
2659
2660       if (logging != MagickFalse)
2661         if (image->page.x || image->page.y)
2662           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2663             "    Reading PNG oFFs chunk: x: %.20g, y: %.20g.",(double)
2664             image->page.x,(double) image->page.y);
2665     }
2666 #endif
2667 #if defined(PNG_pHYs_SUPPORTED)
2668   if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2669     {
2670       if (mng_info->have_global_phys)
2671         {
2672           png_set_pHYs(ping,ping_info,
2673                        mng_info->global_x_pixels_per_unit,
2674                        mng_info->global_y_pixels_per_unit,
2675                        mng_info->global_phys_unit_type);
2676         }
2677     }
2678
2679   x_resolution=0;
2680   y_resolution=0;
2681   unit_type=0;
2682   if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
2683     {
2684       /*
2685         Set image resolution.
2686       */
2687       (void) png_get_pHYs(ping,ping_info,&x_resolution,&y_resolution,
2688         &unit_type);
2689       image->resolution.x=(double) x_resolution;
2690       image->resolution.y=(double) y_resolution;
2691
2692       if (unit_type == PNG_RESOLUTION_METER)
2693         {
2694           image->units=PixelsPerCentimeterResolution;
2695           image->resolution.x=(double) x_resolution/100.0;
2696           image->resolution.y=(double) y_resolution/100.0;
2697         }
2698
2699       if (logging != MagickFalse)
2700         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2701           "    Reading PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
2702           (double) x_resolution,(double) y_resolution,unit_type);
2703     }
2704 #endif
2705
2706   if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
2707     {
2708       png_colorp
2709         palette;
2710
2711       (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2712
2713       if ((number_colors == 0) &&
2714           ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
2715         {
2716           if (mng_info->global_plte_length)
2717             {
2718               png_set_PLTE(ping,ping_info,mng_info->global_plte,
2719                 (int) mng_info->global_plte_length);
2720
2721               if (!png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2722               {
2723                 if (mng_info->global_trns_length)
2724                   {
2725                     png_warning(ping,
2726                       "global tRNS has more entries than global PLTE");
2727                   }
2728                 else
2729                   {
2730                      png_set_tRNS(ping,ping_info,mng_info->global_trns,
2731                        (int) mng_info->global_trns_length,NULL);
2732                   }
2733                }
2734 #ifdef PNG_READ_bKGD_SUPPORTED
2735               if (
2736 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2737                    mng_info->have_saved_bkgd_index ||
2738 #endif
2739                    png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2740                     {
2741                       png_color_16
2742                          background;
2743
2744 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
2745                       if (mng_info->have_saved_bkgd_index)
2746                         background.index=mng_info->saved_bkgd_index;
2747 #endif
2748                       if (png_get_valid(ping, ping_info, PNG_INFO_bKGD))
2749                         background.index=ping_background->index;
2750
2751                       background.red=(png_uint_16)
2752                         mng_info->global_plte[background.index].red;
2753
2754                       background.green=(png_uint_16)
2755                         mng_info->global_plte[background.index].green;
2756
2757                       background.blue=(png_uint_16)
2758                         mng_info->global_plte[background.index].blue;
2759
2760                       background.gray=(png_uint_16)
2761                         mng_info->global_plte[background.index].green;
2762
2763                       png_set_bKGD(ping,ping_info,&background);
2764                     }
2765 #endif
2766                 }
2767               else
2768                 png_error(ping,"No global PLTE in file");
2769             }
2770         }
2771
2772 #ifdef PNG_READ_bKGD_SUPPORTED
2773   if (mng_info->have_global_bkgd &&
2774           (!png_get_valid(ping,ping_info,PNG_INFO_bKGD)))
2775       image->background_color=mng_info->mng_global_bkgd;
2776
2777   if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
2778     {
2779       unsigned int
2780         bkgd_scale;
2781
2782       /* Set image background color.
2783        * Scale background components to 16-bit, then scale
2784        * to quantum depth
2785        */
2786
2787         bkgd_scale = 1;
2788
2789         if (ping_file_depth == 1)
2790            bkgd_scale = 255;
2791
2792         else if (ping_file_depth == 2)
2793            bkgd_scale = 85;
2794
2795         else if (ping_file_depth == 4)
2796            bkgd_scale = 17;
2797
2798         if (ping_file_depth <= 8)
2799            bkgd_scale *= 257;
2800
2801         ping_background->red *= bkgd_scale;
2802         ping_background->green *= bkgd_scale;
2803         ping_background->blue *= bkgd_scale;
2804
2805         if (logging != MagickFalse)
2806           {
2807             if (logging != MagickFalse)
2808               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2809                  "    Reading PNG bKGD chunk, raw ping_background=(%d,%d,%d).\n"
2810                  "    bkgd_scale=%d.  ping_background=(%d,%d,%d).",
2811                  ping_background->red,ping_background->green,
2812                  ping_background->blue,
2813                  bkgd_scale,ping_background->red,
2814                  ping_background->green,ping_background->blue);
2815           }
2816
2817         image->background_color.red=
2818             ScaleShortToQuantum(ping_background->red);
2819
2820         image->background_color.green=
2821             ScaleShortToQuantum(ping_background->green);
2822
2823         image->background_color.blue=
2824           ScaleShortToQuantum(ping_background->blue);
2825
2826         image->background_color.alpha=OpaqueAlpha;
2827
2828         if (logging != MagickFalse)
2829           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2830             "    image->background_color=(%.20g,%.20g,%.20g).",
2831             (double) image->background_color.red,
2832             (double) image->background_color.green,
2833             (double) image->background_color.blue);
2834     }
2835 #endif /* PNG_READ_bKGD_SUPPORTED */
2836
2837   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
2838     {
2839       /*
2840         Image has a tRNS chunk.
2841       */
2842       int
2843         max_sample;
2844
2845       size_t
2846         one=1;
2847
2848       if (logging != MagickFalse)
2849         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2850           "    Reading PNG tRNS chunk.");
2851
2852       max_sample = (int) ((one << ping_file_depth) - 1);
2853
2854       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
2855           (int)ping_trans_color->gray > max_sample) ||
2856           (ping_color_type == PNG_COLOR_TYPE_RGB &&
2857           ((int)ping_trans_color->red > max_sample ||
2858           (int)ping_trans_color->green > max_sample ||
2859           (int)ping_trans_color->blue > max_sample)))
2860         {
2861           if (logging != MagickFalse)
2862             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2863               "    Ignoring PNG tRNS chunk with out-of-range sample.");
2864           png_free_data(ping, ping_info, PNG_FREE_TRNS, 0);
2865           png_set_invalid(ping,ping_info,PNG_INFO_tRNS);
2866           image->alpha_trait=UndefinedPixelTrait;
2867         }
2868       else
2869         {
2870           int
2871             scale_to_short;
2872
2873           scale_to_short = 65535L/((1UL << ping_file_depth)-1);
2874
2875           /* Scale transparent_color to short */
2876           transparent_color.red= scale_to_short*ping_trans_color->red;
2877           transparent_color.green= scale_to_short*ping_trans_color->green;
2878           transparent_color.blue= scale_to_short*ping_trans_color->blue;
2879           transparent_color.alpha= scale_to_short*ping_trans_color->gray;
2880
2881           if (ping_color_type == PNG_COLOR_TYPE_GRAY)
2882             {
2883               if (logging != MagickFalse)
2884               {
2885                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2886                   "    Raw tRNS graylevel = %d, scaled graylevel = %d.",
2887                   (int) ping_trans_color->gray,(int) transparent_color.alpha);
2888
2889               }
2890               transparent_color.red=transparent_color.alpha;
2891               transparent_color.green=transparent_color.alpha;
2892               transparent_color.blue=transparent_color.alpha;
2893             }
2894         }
2895     }
2896 #if defined(PNG_READ_sBIT_SUPPORTED)
2897   if (mng_info->have_global_sbit)
2898     {
2899       if (!png_get_valid(ping,ping_info,PNG_INFO_sBIT))
2900         png_set_sBIT(ping,ping_info,&mng_info->global_sbit);
2901     }
2902 #endif
2903   num_passes=png_set_interlace_handling(ping);
2904
2905   png_read_update_info(ping,ping_info);
2906
2907   ping_rowbytes=png_get_rowbytes(ping,ping_info);
2908
2909   /*
2910     Initialize image structure.
2911   */
2912   mng_info->image_box.left=0;
2913   mng_info->image_box.right=(ssize_t) ping_width;
2914   mng_info->image_box.top=0;
2915   mng_info->image_box.bottom=(ssize_t) ping_height;
2916   if (mng_info->mng_type == 0)
2917     {
2918       mng_info->mng_width=ping_width;
2919       mng_info->mng_height=ping_height;
2920       mng_info->frame=mng_info->image_box;
2921       mng_info->clip=mng_info->image_box;
2922     }
2923
2924   else
2925     {
2926       image->page.y=mng_info->y_off[mng_info->object_id];
2927     }
2928
2929   image->compression=ZipCompression;
2930   image->columns=ping_width;
2931   image->rows=ping_height;
2932
2933   if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
2934       ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
2935     {
2936       double
2937         image_gamma = image->gamma;
2938
2939       (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2940          "    image->gamma=%f",(float) image_gamma);
2941
2942       if (image_gamma > 0.75)
2943         {
2944           /* Set image->rendering_intent to Undefined,
2945            * image->colorspace to GRAY, and reset image->chromaticity.
2946            */
2947           image->intensity = Rec709LuminancePixelIntensityMethod;
2948           SetImageColorspace(image,GRAYColorspace,exception);
2949         }
2950       else
2951         {
2952           RenderingIntent
2953             save_rendering_intent = image->rendering_intent;
2954           ChromaticityInfo
2955             save_chromaticity = image->chromaticity;
2956
2957           SetImageColorspace(image,GRAYColorspace,exception);
2958           image->rendering_intent = save_rendering_intent;
2959           image->chromaticity = save_chromaticity;
2960         }
2961
2962       image->gamma = image_gamma;
2963     }
2964
2965   (void)LogMagickEvent(CoderEvent,GetMagickModule(),
2966       "    image->colorspace=%d",(int) image->colorspace);
2967
2968   if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
2969       ((int) ping_bit_depth < 16 &&
2970       (int) ping_color_type == PNG_COLOR_TYPE_GRAY))
2971     {
2972       size_t
2973         one;
2974
2975       image->storage_class=PseudoClass;
2976       one=1;
2977       image->colors=one << ping_file_depth;
2978 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
2979       if (image->colors > 256)
2980         image->colors=256;
2981 #else
2982       if (image->colors > 65536L)
2983         image->colors=65536L;
2984 #endif
2985       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
2986         {
2987           png_colorp
2988             palette;
2989
2990           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
2991           image->colors=(size_t) number_colors;
2992
2993           if (logging != MagickFalse)
2994             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2995               "    Reading PNG PLTE chunk: number_colors: %d.",number_colors);
2996         }
2997     }
2998
2999   if (image->storage_class == PseudoClass)
3000     {
3001       /*
3002         Initialize image colormap.
3003       */
3004       if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
3005         png_error(ping,"Memory allocation failed");
3006
3007       if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3008         {
3009           png_colorp
3010             palette;
3011
3012           (void) png_get_PLTE(ping,ping_info,&palette,&number_colors);
3013
3014           for (i=0; i < (ssize_t) number_colors; i++)
3015           {
3016             image->colormap[i].red=ScaleCharToQuantum(palette[i].red);
3017             image->colormap[i].green=ScaleCharToQuantum(palette[i].green);
3018             image->colormap[i].blue=ScaleCharToQuantum(palette[i].blue);
3019           }
3020
3021           for ( ; i < (ssize_t) image->colors; i++)
3022           {
3023             image->colormap[i].red=0;
3024             image->colormap[i].green=0;
3025             image->colormap[i].blue=0;
3026           }
3027         }
3028
3029       else
3030         {
3031           Quantum
3032             scale;
3033
3034           scale = (Quantum) (65535.0/((1UL << ping_file_depth)-1.0));
3035
3036 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
3037           scale = ScaleShortToQuantum(scale);
3038 #endif
3039
3040           for (i=0; i < (ssize_t) image->colors; i++)
3041           {
3042             image->colormap[i].red=(Quantum) (i*scale);
3043             image->colormap[i].green=(Quantum) (i*scale);
3044             image->colormap[i].blue=(Quantum) (i*scale);
3045           }
3046        }
3047     }
3048
3049    /* Set some properties for reporting by "identify" */
3050     {
3051       char
3052         msg[MagickPathExtent];
3053
3054      /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
3055         ping_interlace_method in value */
3056
3057      (void) FormatLocaleString(msg,MagickPathExtent,
3058          "%d, %d",(int) ping_width, (int) ping_height);
3059      (void) SetImageProperty(image,"png:IHDR.width,height",msg,exception);
3060
3061      (void) FormatLocaleString(msg,MagickPathExtent,"%d",(int) ping_file_depth);
3062      (void) SetImageProperty(image,"png:IHDR.bit_depth",msg,exception);
3063
3064      (void) FormatLocaleString(msg,MagickPathExtent,"%d (%s)",
3065          (int) ping_color_type,
3066          Magick_ColorType_from_PNG_ColorType((int)ping_color_type));
3067      (void) SetImageProperty(image,"png:IHDR.color_type",msg,exception);
3068
3069      if (ping_interlace_method == 0)
3070        {
3071          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)",
3072             (int) ping_interlace_method);
3073        }
3074      else if (ping_interlace_method == 1)
3075        {
3076          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Adam7 method)",
3077             (int) ping_interlace_method);
3078        }
3079      else
3080        {
3081          (void) FormatLocaleString(msg,MagickPathExtent,"%d (Unknown method)",
3082             (int) ping_interlace_method);
3083        }
3084        (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);
3085
3086      if (number_colors != 0)
3087        {
3088          (void) FormatLocaleString(msg,MagickPathExtent,"%d",
3089             (int) number_colors);
3090          (void) SetImageProperty(image,"png:PLTE.number_colors",msg,
3091             exception);
3092        }
3093    }
3094 #if defined(PNG_tIME_SUPPORTED)
3095    read_tIME_chunk(image,ping,ping_info,exception);
3096 #endif
3097
3098
3099   /*
3100     Read image scanlines.
3101   */
3102   if (image->delay != 0)
3103     mng_info->scenes_found++;
3104
3105   if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3106       (image_info->number_scenes != 0) && (mng_info->scenes_found > (ssize_t)
3107       (image_info->first_scene+image_info->number_scenes))))
3108     {
3109       /* This happens later in non-ping decodes */
3110       if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3111         image->storage_class=DirectClass;
3112       image->alpha_trait=
3113         (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3114          ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3115          (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3116         BlendPixelTrait : UndefinedPixelTrait;
3117
3118       if (logging != MagickFalse)
3119         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3120           "    Skipping PNG image data for scene %.20g",(double)
3121           mng_info->scenes_found-1);
3122       png_destroy_read_struct(&ping,&ping_info,&end_info);
3123
3124 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3125       UnlockSemaphoreInfo(ping_semaphore);
3126 #endif
3127
3128       if (logging != MagickFalse)
3129         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3130           "  exit ReadOnePNGImage().");
3131
3132       return(image);
3133     }
3134
3135   if (logging != MagickFalse)
3136     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3137       "    Reading PNG IDAT chunk(s)");
3138
3139   status=SetImageExtent(image,image->columns,image->rows,exception);
3140   if (status == MagickFalse)
3141     return(DestroyImageList(image));
3142
3143   if (num_passes > 1)
3144     pixel_info=AcquireVirtualMemory(image->rows,ping_rowbytes*
3145       sizeof(*ping_pixels));
3146   else
3147     pixel_info=AcquireVirtualMemory(ping_rowbytes,sizeof(*ping_pixels));
3148
3149   if (pixel_info == (MemoryInfo *) NULL)
3150     png_error(ping,"Memory allocation failed");
3151   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
3152
3153   if (logging != MagickFalse)
3154     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3155       "    Converting PNG pixels to pixel packets");
3156   /*
3157     Convert PNG pixels to pixel packets.
3158   */
3159   quantum_info=AcquireQuantumInfo(image_info,image);
3160
3161   if (quantum_info == (QuantumInfo *) NULL)
3162      png_error(ping,"Failed to allocate quantum_info");
3163
3164   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
3165
3166   {
3167
3168    MagickBooleanType
3169      found_transparent_pixel;
3170
3171   found_transparent_pixel=MagickFalse;
3172
3173   if (image->storage_class == DirectClass)
3174     {
3175       for (pass=0; pass < num_passes; pass++)
3176       {
3177         /*
3178           Convert image to DirectClass pixel packets.
3179         */
3180         image->alpha_trait=
3181             (((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3182             ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3183             (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3184             BlendPixelTrait : UndefinedPixelTrait;
3185
3186         for (y=0; y < (ssize_t) image->rows; y++)
3187         {
3188           if (num_passes > 1)
3189             row_offset=ping_rowbytes*y;
3190
3191           else
3192             row_offset=0;
3193
3194           png_read_row(ping,ping_pixels+row_offset,NULL);
3195
3196           if (pass < num_passes-1)
3197             continue;
3198
3199           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3200
3201           if (q == (Quantum *) NULL)
3202             break;
3203
3204           if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY)
3205             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3206               GrayQuantum,ping_pixels+row_offset,exception);
3207
3208           else if ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3209             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3210               GrayAlphaQuantum,ping_pixels+row_offset,exception);
3211
3212           else if ((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
3213             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3214               RGBAQuantum,ping_pixels+row_offset,exception);
3215
3216           else if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3217             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3218               IndexQuantum,ping_pixels+row_offset,exception);
3219
3220           else /* ping_color_type == PNG_COLOR_TYPE_RGB */
3221             (void) ImportQuantumPixels(image,(CacheView *) NULL,quantum_info,
3222               RGBQuantum,ping_pixels+row_offset,exception);
3223
3224           if (found_transparent_pixel == MagickFalse)
3225             {
3226               /* Is there a transparent pixel in the row? */
3227               if (y== 0 && logging != MagickFalse)
3228                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3229                    "    Looking for cheap transparent pixel");
3230
3231               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3232               {
3233                 if ((ping_color_type == PNG_COLOR_TYPE_RGBA ||
3234                     ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) &&
3235                    (GetPixelAlpha(image,q) != OpaqueAlpha))
3236                   {
3237                     if (logging != MagickFalse)
3238                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3239                         "    ...got one.");
3240
3241                     found_transparent_pixel = MagickTrue;
3242                     break;
3243                   }
3244                 if ((ping_color_type == PNG_COLOR_TYPE_RGB ||
3245                     ping_color_type == PNG_COLOR_TYPE_GRAY) &&
3246                     (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3247                     transparent_color.red &&
3248                     ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3249                     transparent_color.green &&
3250                     ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3251                     transparent_color.blue))
3252                   {
3253                     if (logging != MagickFalse)
3254                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3255                         "    ...got one.");
3256                     found_transparent_pixel = MagickTrue;
3257                     break;
3258                   }
3259                 q+=GetPixelChannels(image);
3260               }
3261             }
3262
3263           if (num_passes == 1)
3264             {
3265               status=SetImageProgress(image,LoadImageTag,
3266                   (MagickOffsetType) y, image->rows);
3267
3268               if (status == MagickFalse)
3269                 break;
3270             }
3271           if (SyncAuthenticPixels(image,exception) == MagickFalse)
3272             break;
3273         }
3274
3275         if (num_passes != 1)
3276           {
3277             status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3278             if (status == MagickFalse)
3279               break;
3280           }
3281       }
3282     }
3283
3284   else /* image->storage_class != DirectClass */
3285
3286     for (pass=0; pass < num_passes; pass++)
3287     {
3288       Quantum
3289         *quantum_scanline;
3290
3291       register Quantum
3292         *r;
3293
3294       /*
3295         Convert grayscale image to PseudoClass pixel packets.
3296       */
3297       if (logging != MagickFalse)
3298         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3299           "    Converting grayscale pixels to pixel packets");
3300
3301       image->alpha_trait=ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ?
3302         BlendPixelTrait : UndefinedPixelTrait;
3303
3304       quantum_scanline=(Quantum *) AcquireQuantumMemory(image->columns,
3305         (image->alpha_trait  == BlendPixelTrait?  2 : 1)*
3306         sizeof(*quantum_scanline));
3307
3308       if (quantum_scanline == (Quantum *) NULL)
3309         png_error(ping,"Memory allocation failed");
3310
3311       for (y=0; y < (ssize_t) image->rows; y++)
3312       {
3313         Quantum
3314            alpha;
3315
3316         if (num_passes > 1)
3317           row_offset=ping_rowbytes*y;
3318
3319         else
3320           row_offset=0;
3321
3322         png_read_row(ping,ping_pixels+row_offset,NULL);
3323
3324         if (pass < num_passes-1)
3325           continue;
3326
3327         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
3328
3329         if (q == (Quantum *) NULL)
3330           break;
3331
3332         p=ping_pixels+row_offset;
3333         r=quantum_scanline;
3334
3335         switch (ping_bit_depth)
3336         {
3337           case 8:
3338           {
3339
3340             if (ping_color_type == 4)
3341               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3342               {
3343                 *r++=*p++;
3344
3345                 alpha=ScaleCharToQuantum((unsigned char)*p++);
3346
3347                 SetPixelAlpha(image,alpha,q);
3348
3349                 if (alpha != OpaqueAlpha)
3350                   found_transparent_pixel = MagickTrue;
3351
3352                 q+=GetPixelChannels(image);
3353               }
3354
3355             else
3356               for (x=(ssize_t) image->columns-1; x >= 0; x--)
3357                 *r++=*p++;
3358
3359             break;
3360           }
3361
3362           case 16:
3363           {
3364             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3365             {
3366 #if (MAGICKCORE_QUANTUM_DEPTH >= 16)
3367               unsigned short
3368                 quantum;
3369
3370               if (image->colors > 256)
3371                 quantum=((*p++) << 8);
3372
3373               else
3374                 quantum=0;
3375
3376               quantum|=(*p++);
3377               *r=ScaleShortToQuantum(quantum);
3378               r++;
3379
3380               if (ping_color_type == 4)
3381                 {
3382                   if (image->colors > 256)
3383                     quantum=((*p++) << 8);
3384                   else
3385                     quantum=0;
3386
3387                   quantum|=(*p++);
3388
3389                   alpha=ScaleShortToQuantum(quantum);
3390                   SetPixelAlpha(image,alpha,q);
3391
3392                   if (alpha != OpaqueAlpha)
3393                     found_transparent_pixel = MagickTrue;
3394
3395                   q+=GetPixelChannels(image);
3396                 }
3397
3398 #else /* MAGICKCORE_QUANTUM_DEPTH == 8 */
3399               *r++=(*p++);
3400               p++; /* strip low byte */
3401
3402               if (ping_color_type == 4)
3403                 {
3404                   SetPixelAlpha(image,*p++,q);
3405
3406                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
3407                     found_transparent_pixel = MagickTrue;
3408
3409                   p++;
3410                   q+=GetPixelChannels(image);
3411                 }
3412 #endif
3413             }
3414
3415             break;
3416           }
3417
3418           default:
3419             break;
3420         }
3421
3422         /*
3423           Transfer image scanline.
3424         */
3425         r=quantum_scanline;
3426
3427         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3428
3429         if (q == (Quantum *) NULL)
3430           break;
3431         for (x=0; x < (ssize_t) image->columns; x++)
3432         {
3433           SetPixelIndex(image,*r++,q);
3434           q+=GetPixelChannels(image);
3435         }
3436
3437         if (SyncAuthenticPixels(image,exception) == MagickFalse)
3438           break;
3439
3440         if (num_passes == 1)
3441           {
3442             status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
3443               image->rows);
3444
3445             if (status == MagickFalse)
3446               break;
3447           }
3448       }
3449
3450       if (num_passes != 1)
3451         {
3452           status=SetImageProgress(image,LoadImageTag,pass,num_passes);
3453
3454           if (status == MagickFalse)
3455             break;
3456         }
3457
3458       quantum_scanline=(Quantum *) RelinquishMagickMemory(quantum_scanline);
3459     }
3460
3461     image->alpha_trait=found_transparent_pixel ? BlendPixelTrait :
3462       UndefinedPixelTrait;
3463
3464     if (logging != MagickFalse)
3465       {
3466         if (found_transparent_pixel != MagickFalse)
3467           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3468             "    Found transparent pixel");
3469         else
3470           {
3471             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3472               "    No transparent pixel was found");
3473
3474             ping_color_type&=0x03;
3475           }
3476       }
3477     }
3478
3479   if (quantum_info != (QuantumInfo *) NULL)
3480     quantum_info=DestroyQuantumInfo(quantum_info);
3481
3482   if (image->storage_class == PseudoClass)
3483     {
3484       PixelTrait
3485         alpha_trait;
3486
3487       alpha_trait=image->alpha_trait;
3488       image->alpha_trait=UndefinedPixelTrait;
3489       (void) SyncImage(image,exception);
3490       image->alpha_trait=alpha_trait;
3491     }
3492
3493   png_read_end(ping,end_info);
3494
3495   if (logging != MagickFalse)
3496   {
3497     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3498        "  image->storage_class=%d\n",(int) image->storage_class);
3499   }
3500
3501   if (image_info->number_scenes != 0 && mng_info->scenes_found-1 <
3502       (ssize_t) image_info->first_scene && image->delay != 0)
3503     {
3504       png_destroy_read_struct(&ping,&ping_info,&end_info);
3505       pixel_info=RelinquishVirtualMemory(pixel_info);
3506       image->colors=2;
3507       (void) SetImageBackgroundColor(image,exception);
3508 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3509       UnlockSemaphoreInfo(ping_semaphore);
3510 #endif
3511       if (logging != MagickFalse)
3512         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3513           "  exit ReadOnePNGImage() early.");
3514       return(image);
3515     }
3516
3517   if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3518     {
3519       ClassType
3520         storage_class;
3521
3522       /*
3523         Image has a transparent background.
3524       */
3525       storage_class=image->storage_class;
3526       image->alpha_trait=BlendPixelTrait;
3527
3528 /* Balfour fix from imagemagick discourse server, 5 Feb 2010 */
3529
3530       if (storage_class == PseudoClass)
3531         {
3532           if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
3533             {
3534               for (x=0; x < ping_num_trans; x++)
3535               {
3536                  image->colormap[x].alpha_trait=BlendPixelTrait;
3537                  image->colormap[x].alpha =
3538                    ScaleCharToQuantum((unsigned char)ping_trans_alpha[x]);
3539               }
3540             }
3541
3542           else if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3543             {
3544               for (x=0; x < (int) image->colors; x++)
3545               {
3546                  if (ScaleQuantumToShort(image->colormap[x].red) ==
3547                      transparent_color.alpha)
3548                  {
3549                     image->colormap[x].alpha_trait=BlendPixelTrait;
3550                     image->colormap[x].alpha = (Quantum) TransparentAlpha;
3551                  }
3552               }
3553             }
3554           (void) SyncImage(image,exception);
3555         }
3556
3557 #if 1 /* Should have already been done above, but glennrp problem P10
3558        * needs this.
3559        */
3560       else
3561         {
3562           for (y=0; y < (ssize_t) image->rows; y++)
3563           {
3564             image->storage_class=storage_class;
3565             q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
3566
3567             if (q == (Quantum *) NULL)
3568               break;
3569
3570
3571             /* Caution: on a Q8 build, this does not distinguish between
3572              * 16-bit colors that differ only in the low byte
3573              */
3574             for (x=(ssize_t) image->columns-1; x >= 0; x--)
3575             {
3576               if (ScaleQuantumToShort(GetPixelRed(image,q)) ==
3577                   transparent_color.red &&
3578                   ScaleQuantumToShort(GetPixelGreen(image,q)) ==
3579                   transparent_color.green &&
3580                   ScaleQuantumToShort(GetPixelBlue(image,q)) ==
3581                   transparent_color.blue)
3582                 {
3583                   SetPixelAlpha(image,TransparentAlpha,q);
3584                 }
3585
3586 #if 0 /* I have not found a case where this is needed. */
3587               else
3588                 {
3589                   SetPixelAlpha(image,q)=(Quantum) OpaqueAlpha;
3590                 }
3591 #endif
3592
3593               q+=GetPixelChannels(image);
3594             }
3595
3596             if (SyncAuthenticPixels(image,exception) == MagickFalse)
3597                break;
3598           }
3599         }
3600 #endif
3601
3602       image->storage_class=DirectClass;
3603     }
3604
3605   for (j = 0; j < 2; j++)
3606   {
3607     if (j == 0)
3608       status = png_get_text(ping,ping_info,&text,&num_text) != 0 ?
3609           MagickTrue : MagickFalse;
3610     else
3611       status = png_get_text(ping,end_info,&text,&num_text) != 0 ?
3612           MagickTrue : MagickFalse;
3613
3614     if (status != MagickFalse)
3615       for (i=0; i < (ssize_t) num_text; i++)
3616       {
3617         /* Check for a profile */
3618
3619         if (logging != MagickFalse)
3620           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3621             "    Reading PNG text chunk");
3622
3623         if (strlen(text[i].key) > 16 &&
3624             memcmp(text[i].key, "Raw profile type ",17) == 0)
3625           {
3626             const char
3627               *value;
3628
3629             value=GetImageOption(image_info,"profile:skip");
3630
3631             if (IsOptionMember(text[i].key+17,value) == MagickFalse)
3632             {
3633                (void) Magick_png_read_raw_profile(ping,image,image_info,text,
3634                   (int) i,exception);
3635                num_raw_profiles++;
3636                if (logging != MagickFalse)
3637                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3638                    "    Read raw profile %s",text[i].key+17);
3639             }
3640             else
3641             {
3642                if (logging != MagickFalse)
3643                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3644                    "    Skipping raw profile %s",text[i].key+17);
3645             }
3646           }
3647
3648         else
3649           {
3650             char
3651               *value;
3652
3653             length=text[i].text_length;
3654             value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
3655               sizeof(*value));
3656             if (value == (char *) NULL)
3657               {
3658                 png_error(ping,"Memory allocation failed");
3659                 break;
3660               }
3661             *value='\0';
3662             (void) ConcatenateMagickString(value,text[i].text,length+2);
3663
3664             /* Don't save "density" or "units" property if we have a pHYs
3665              * chunk
3666              */
3667             if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
3668                 (LocaleCompare(text[i].key,"density") != 0 &&
3669                 LocaleCompare(text[i].key,"units") != 0))
3670                (void) SetImageProperty(image,text[i].key,value,exception);
3671
3672             if (logging != MagickFalse)
3673             {
3674               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3675                 "      length: %lu\n"
3676                 "      Keyword: %s",
3677                 (unsigned long) length,
3678                 text[i].key);
3679             }
3680
3681             value=DestroyString(value);
3682           }
3683       }
3684     num_text_total += num_text;
3685   }
3686
3687 #ifdef MNG_OBJECT_BUFFERS
3688   /*
3689     Store the object if necessary.
3690   */
3691   if (object_id && !mng_info->frozen[object_id])
3692     {
3693       if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3694         {
3695           /*
3696             create a new object buffer.
3697           */
3698           mng_info->ob[object_id]=(MngBuffer *)
3699             AcquireMagickMemory(sizeof(MngBuffer));
3700
3701           if (mng_info->ob[object_id] != (MngBuffer *) NULL)
3702             {
3703               mng_info->ob[object_id]->image=(Image *) NULL;
3704               mng_info->ob[object_id]->reference_count=1;
3705             }
3706         }
3707
3708       if ((mng_info->ob[object_id] == (MngBuffer *) NULL) ||
3709           mng_info->ob[object_id]->frozen)
3710         {
3711           if (mng_info->ob[object_id] == (MngBuffer *) NULL)
3712              png_error(ping,"Memory allocation failed");
3713
3714           if (mng_info->ob[object_id]->frozen)
3715             png_error(ping,"Cannot overwrite frozen MNG object buffer");
3716         }
3717
3718       else
3719         {
3720
3721           if (mng_info->ob[object_id]->image != (Image *) NULL)
3722             mng_info->ob[object_id]->image=DestroyImage
3723                 (mng_info->ob[object_id]->image);
3724
3725           mng_info->ob[object_id]->image=CloneImage(image,0,0,MagickTrue,
3726             exception);
3727
3728           if (mng_info->ob[object_id]->image != (Image *) NULL)
3729             mng_info->ob[object_id]->image->file=(FILE *) NULL;
3730
3731           else
3732             png_error(ping, "Cloning image for object buffer failed");
3733
3734           if (ping_width > 250000L || ping_height > 250000L)
3735              png_error(ping,"PNG Image dimensions are too large.");
3736
3737           mng_info->ob[object_id]->width=ping_width;
3738           mng_info->ob[object_id]->height=ping_height;
3739           mng_info->ob[object_id]->color_type=ping_color_type;
3740           mng_info->ob[object_id]->sample_depth=ping_bit_depth;
3741           mng_info->ob[object_id]->interlace_method=ping_interlace_method;
3742           mng_info->ob[object_id]->compression_method=
3743              ping_compression_method;
3744           mng_info->ob[object_id]->filter_method=ping_filter_method;
3745
3746           if (png_get_valid(ping,ping_info,PNG_INFO_PLTE))
3747             {
3748               png_colorp
3749                 plte;
3750
3751               /*
3752                 Copy the PLTE to the object buffer.
3753               */
3754               png_get_PLTE(ping,ping_info,&plte,&number_colors);
3755               mng_info->ob[object_id]->plte_length=number_colors;
3756
3757               for (i=0; i < number_colors; i++)
3758               {
3759                 mng_info->ob[object_id]->plte[i]=plte[i];
3760               }
3761             }
3762
3763           else
3764               mng_info->ob[object_id]->plte_length=0;
3765         }
3766     }
3767 #endif
3768
3769    /* Set image->alpha_trait to MagickTrue if the input colortype supports
3770     * alpha or if a valid tRNS chunk is present, no matter whether there
3771     * is actual transparency present.
3772     */
3773     image->alpha_trait=(((int) ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA) ||
3774         ((int) ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA) ||
3775         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
3776         BlendPixelTrait : UndefinedPixelTrait;
3777
3778 #if 0  /* I'm not sure what's wrong here but it does not work. */
3779     if (image->alpha_trait != UndefinedPixelTrait)
3780     {
3781       if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
3782         (void) SetImageType(image,GrayscaleAlphaType,exception);
3783
3784       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3785         (void) SetImageType(image,PaletteAlphaType,exception);
3786
3787       else
3788         (void) SetImageType(image,TrueColorAlphaType,exception);
3789     }
3790
3791     else
3792     {
3793       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
3794         (void) SetImageType(image,GrayscaleType,exception);
3795
3796       else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
3797         (void) SetImageType(image,PaletteType,exception);
3798
3799       else
3800         (void) SetImageType(image,TrueColorType,exception);
3801     }
3802 #endif
3803
3804    /* Set more properties for identify to retrieve */
3805    {
3806      char
3807        msg[MagickPathExtent];
3808
3809      if (num_text_total != 0)
3810        {
3811          /* libpng doesn't tell us whether they were tEXt, zTXt, or iTXt */
3812          (void) FormatLocaleString(msg,MagickPathExtent,
3813             "%d tEXt/zTXt/iTXt chunks were found", num_text_total);
3814          (void) SetImageProperty(image,"png:text",msg,
3815                 exception);
3816        }
3817
3818      if (num_raw_profiles != 0)
3819        {
3820          (void) FormatLocaleString(msg,MagickPathExtent,
3821             "%d were found", num_raw_profiles);
3822          (void) SetImageProperty(image,"png:text-encoded profiles",msg,
3823                 exception);
3824        }
3825
3826      if (ping_found_cHRM != MagickFalse)
3827        {
3828          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3829             "chunk was found (see Chromaticity, above)");
3830          (void) SetImageProperty(image,"png:cHRM",msg,
3831                 exception);
3832        }
3833
3834      if (png_get_valid(ping,ping_info,PNG_INFO_bKGD))
3835        {
3836          (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3837             "chunk was found (see Background color, above)");
3838          (void) SetImageProperty(image,"png:bKGD",msg,
3839                 exception);
3840        }
3841
3842      (void) FormatLocaleString(msg,MagickPathExtent,"%s",
3843         "chunk was found");
3844
3845 #if defined(PNG_iCCP_SUPPORTED)
3846      if (ping_found_iCCP != MagickFalse)
3847         (void) SetImageProperty(image,"png:iCCP",msg,
3848                 exception);
3849 #endif
3850
3851      if (png_get_valid(ping,ping_info,PNG_INFO_tRNS))
3852         (void) SetImageProperty(image,"png:tRNS",msg,
3853                 exception);
3854
3855 #if defined(PNG_sRGB_SUPPORTED)
3856      if (ping_found_sRGB != MagickFalse)
3857        {
3858          (void) FormatLocaleString(msg,MagickPathExtent,
3859             "intent=%d (%s)",
3860             (int) intent,
3861             Magick_RenderingIntentString_from_PNG_RenderingIntent(intent));
3862          (void) SetImageProperty(image,"png:sRGB",msg,
3863                  exception);
3864        }
3865 #endif
3866
3867      if (ping_found_gAMA != MagickFalse)
3868        {
3869          (void) FormatLocaleString(msg,MagickPathExtent,
3870             "gamma=%.8g (See Gamma, above)",
3871             file_gamma);
3872          (void) SetImageProperty(image,"png:gAMA",msg,
3873                 exception);
3874        }
3875
3876 #if defined(PNG_pHYs_SUPPORTED)
3877      if (png_get_valid(ping,ping_info,PNG_INFO_pHYs))
3878        {
3879          (void) FormatLocaleString(msg,MagickPathExtent,
3880             "x_res=%.10g, y_res=%.10g, units=%d",
3881             (double) x_resolution,(double) y_resolution, unit_type);
3882          (void) SetImageProperty(image,"png:pHYs",msg,
3883                 exception);
3884        }
3885 #endif
3886
3887 #if defined(PNG_oFFs_SUPPORTED)
3888      if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
3889        {
3890          (void) FormatLocaleString(msg,MagickPathExtent,"x_off=%.20g, y_off=%.20g",
3891             (double) image->page.x,(double) image->page.y);
3892          (void) SetImageProperty(image,"png:oFFs",msg,
3893                 exception);
3894        }
3895 #endif
3896
3897 #if defined(PNG_tIME_SUPPORTED)
3898      read_tIME_chunk(image,ping,end_info,exception);
3899 #endif
3900
3901      if ((image->page.width != 0 && image->page.width != image->columns) ||
3902          (image->page.height != 0 && image->page.height != image->rows))
3903        {
3904          (void) FormatLocaleString(msg,MagickPathExtent,
3905             "width=%.20g, height=%.20g",
3906             (double) image->page.width,(double) image->page.height);
3907          (void) SetImageProperty(image,"png:vpAg",msg,
3908                 exception);
3909        }
3910    }
3911
3912   /*
3913     Relinquish resources.
3914   */
3915   png_destroy_read_struct(&ping,&ping_info,&end_info);
3916
3917   pixel_info=RelinquishVirtualMemory(pixel_info);
3918
3919   if (logging != MagickFalse)
3920     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
3921       "  exit ReadOnePNGImage()");
3922
3923 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
3924   UnlockSemaphoreInfo(ping_semaphore);
3925 #endif
3926
3927   /* }  for navigation to beginning of SETJMP-protected block, revert to
3928    *    Throwing an Exception when an error occurs.
3929    */
3930
3931   return(image);
3932
3933 /* end of reading one PNG image */
3934 }
3935
3936 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
3937 {
3938   Image
3939     *image;
3940
3941   MagickBooleanType
3942     have_mng_structure,
3943     logging,
3944     status;
3945
3946   MngInfo
3947     *mng_info;
3948
3949   char
3950     magic_number[MagickPathExtent];
3951
3952   ssize_t
3953     count;
3954
3955   /*
3956     Open image file.
3957   */
3958   assert(image_info != (const ImageInfo *) NULL);
3959   assert(image_info->signature == MagickCoreSignature);
3960
3961   if (image_info->debug != MagickFalse)
3962     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
3963       image_info->filename);
3964
3965   assert(exception != (ExceptionInfo *) NULL);
3966   assert(exception->signature == MagickCoreSignature);
3967   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadPNGImage()");
3968   image=AcquireImage(image_info,exception);
3969   mng_info=(MngInfo *) NULL;
3970   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
3971
3972   if (status == MagickFalse)
3973     ThrowReaderException(FileOpenError,"UnableToOpenFile");
3974
3975   /*
3976     Verify PNG signature.
3977   */
3978   count=ReadBlob(image,8,(unsigned char *) magic_number);
3979
3980   if (count < 8 || memcmp(magic_number,"\211PNG\r\n\032\n",8) != 0)
3981     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
3982
3983   /*
3984     Allocate a MngInfo structure.
3985   */
3986   have_mng_structure=MagickFalse;
3987   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
3988
3989   if (mng_info == (MngInfo *) NULL)
3990     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
3991
3992   /*
3993     Initialize members of the MngInfo structure.
3994   */
3995   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
3996   mng_info->image=image;
3997   have_mng_structure=MagickTrue;
3998
3999   image=ReadOnePNGImage(mng_info,image_info,exception);
4000   MngInfoFreeStruct(mng_info,&have_mng_structure);
4001
4002   if (image == (Image *) NULL)
4003     {
4004       if (logging != MagickFalse)
4005         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4006           "exit ReadPNGImage() with error");
4007
4008       return((Image *) NULL);
4009     }
4010
4011   (void) CloseBlob(image);
4012
4013   if ((image->columns == 0) || (image->rows == 0))
4014     {
4015       if (logging != MagickFalse)
4016         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4017           "exit ReadPNGImage() with error.");
4018
4019       ThrowReaderException(CorruptImageError,"CorruptImage");
4020     }
4021
4022   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
4023       ((image->gamma < .45) || (image->gamma > .46)) &&
4024            !(image->chromaticity.red_primary.x>0.6399f &&
4025            image->chromaticity.red_primary.x<0.6401f &&
4026            image->chromaticity.red_primary.y>0.3299f &&
4027            image->chromaticity.red_primary.y<0.3301f &&
4028            image->chromaticity.green_primary.x>0.2999f &&
4029            image->chromaticity.green_primary.x<0.3001f &&
4030            image->chromaticity.green_primary.y>0.5999f &&
4031            image->chromaticity.green_primary.y<0.6001f &&
4032            image->chromaticity.blue_primary.x>0.1499f &&
4033            image->chromaticity.blue_primary.x<0.1501f &&
4034            image->chromaticity.blue_primary.y>0.0599f &&
4035            image->chromaticity.blue_primary.y<0.0601f &&
4036            image->chromaticity.white_point.x>0.3126f &&
4037            image->chromaticity.white_point.x<0.3128f &&
4038            image->chromaticity.white_point.y>0.3289f &&
4039            image->chromaticity.white_point.y<0.3291f))
4040     {
4041        SetImageColorspace(image,RGBColorspace,exception);
4042     }
4043
4044   if (logging != MagickFalse)
4045     {
4046        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4047            "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
4048                (double) image->page.width,(double) image->page.height,
4049                (double) image->page.x,(double) image->page.y);
4050        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4051            "  image->colorspace: %d", (int) image->colorspace);
4052     }
4053
4054   if (logging != MagickFalse)
4055     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadPNGImage()");
4056
4057   return(image);
4058 }
4059
4060
4061
4062 #if defined(JNG_SUPPORTED)
4063 /*
4064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4065 %                                                                             %
4066 %                                                                             %
4067 %                                                                             %
4068 %   R e a d O n e J N G I m a g e                                             %
4069 %                                                                             %
4070 %                                                                             %
4071 %                                                                             %
4072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4073 %
4074 %  ReadOneJNGImage() reads a JPEG Network Graphics (JNG) image file
4075 %  (minus the 8-byte signature)  and returns it.  It allocates the memory
4076 %  necessary for the new Image structure and returns a pointer to the new
4077 %  image.
4078 %
4079 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4080 %
4081 %  The format of the ReadOneJNGImage method is:
4082 %
4083 %      Image *ReadOneJNGImage(MngInfo *mng_info, const ImageInfo *image_info,
4084 %         ExceptionInfo *exception)
4085 %
4086 %  A description of each parameter follows:
4087 %
4088 %    o mng_info: Specifies a pointer to a MngInfo structure.
4089 %
4090 %    o image_info: the image info.
4091 %
4092 %    o exception: return any errors or warnings in this structure.
4093 %
4094 */
4095 static Image *ReadOneJNGImage(MngInfo *mng_info,
4096     const ImageInfo *image_info, ExceptionInfo *exception)
4097 {
4098   Image
4099     *alpha_image,
4100     *color_image,
4101     *image,
4102     *jng_image;
4103
4104   ImageInfo
4105     *alpha_image_info,
4106     *color_image_info;
4107
4108   MagickBooleanType
4109     logging;
4110
4111   ssize_t
4112     y;
4113
4114   MagickBooleanType
4115     status;
4116
4117   png_uint_32
4118     jng_height,
4119     jng_width;
4120
4121   png_byte
4122     jng_color_type,
4123     jng_image_sample_depth,
4124     jng_image_compression_method,
4125     jng_image_interlace_method,
4126     jng_alpha_sample_depth,
4127     jng_alpha_compression_method,
4128     jng_alpha_filter_method,
4129     jng_alpha_interlace_method;
4130
4131   register const Quantum
4132     *s;
4133
4134   register ssize_t
4135     i,
4136     x;
4137
4138   register Quantum
4139     *q;
4140
4141   register unsigned char
4142     *p;
4143
4144   unsigned int
4145     read_JSEP,
4146     reading_idat;
4147
4148   size_t
4149     length;
4150
4151   jng_alpha_compression_method=0;
4152   jng_alpha_sample_depth=8;
4153   jng_color_type=0;
4154   jng_height=0;
4155   jng_width=0;
4156   alpha_image=(Image *) NULL;
4157   color_image=(Image *) NULL;
4158   alpha_image_info=(ImageInfo *) NULL;
4159   color_image_info=(ImageInfo *) NULL;
4160
4161   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
4162     "  Enter ReadOneJNGImage()");
4163
4164   image=mng_info->image;
4165
4166   if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
4167     {
4168       /*
4169         Allocate next image structure.
4170       */
4171       if (logging != MagickFalse)
4172         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4173            "  AcquireNextImage()");
4174
4175       AcquireNextImage(image_info,image,exception);
4176
4177       if (GetNextImageInList(image) == (Image *) NULL)
4178         return((Image *) NULL);
4179
4180       image=SyncNextImageInList(image);
4181     }
4182   mng_info->image=image;
4183
4184   /*
4185     Signature bytes have already been read.
4186   */
4187
4188   read_JSEP=MagickFalse;
4189   reading_idat=MagickFalse;
4190   for (;;)
4191   {
4192     char
4193       type[MagickPathExtent];
4194
4195     unsigned char
4196       *chunk;
4197
4198     unsigned int
4199       count;
4200
4201     /*
4202       Read a new JNG chunk.
4203     */
4204     status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
4205       2*GetBlobSize(image));
4206
4207     if (status == MagickFalse)
4208       break;
4209
4210     type[0]='\0';
4211     (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
4212     length=ReadBlobMSBLong(image);
4213     count=(unsigned int) ReadBlob(image,4,(unsigned char *) type);
4214
4215     if (logging != MagickFalse)
4216       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4217         "  Reading JNG chunk type %c%c%c%c, length: %.20g",
4218         type[0],type[1],type[2],type[3],(double) length);
4219
4220     if (length > PNG_UINT_31_MAX || count == 0)
4221       ThrowReaderException(CorruptImageError,"CorruptImage");
4222
4223     p=NULL;
4224     chunk=(unsigned char *) NULL;
4225
4226     if (length != 0)
4227       {
4228         chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
4229
4230         if (chunk == (unsigned char *) NULL)
4231           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4232
4233         for (i=0; i < (ssize_t) length; i++)
4234           chunk[i]=(unsigned char) ReadBlobByte(image);
4235
4236         p=chunk;
4237       }
4238
4239     (void) ReadBlobMSBLong(image);  /* read crc word */
4240
4241     if (memcmp(type,mng_JHDR,4) == 0)
4242       {
4243         if (length == 16)
4244           {
4245             jng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
4246               (p[2] << 8) | p[3]);
4247             jng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
4248               (p[6] << 8) | p[7]);
4249             if ((jng_width == 0) || (jng_height == 0))
4250               ThrowReaderException(CorruptImageError,"NegativeOrZeroImageSize");
4251             jng_color_type=p[8];
4252             jng_image_sample_depth=p[9];
4253             jng_image_compression_method=p[10];
4254             jng_image_interlace_method=p[11];
4255
4256             image->interlace=jng_image_interlace_method != 0 ? PNGInterlace :
4257               NoInterlace;
4258
4259             jng_alpha_sample_depth=p[12];
4260             jng_alpha_compression_method=p[13];
4261             jng_alpha_filter_method=p[14];
4262             jng_alpha_interlace_method=p[15];
4263
4264             if (logging != MagickFalse)
4265               {
4266                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4267                   "    jng_width:      %16lu,    jng_height:     %16lu\n"
4268                   "    jng_color_type: %16d,     jng_image_sample_depth: %3d\n"
4269                   "    jng_image_compression_method:%3d",
4270                   (unsigned long) jng_width, (unsigned long) jng_height,
4271                   jng_color_type, jng_image_sample_depth,
4272                   jng_image_compression_method);
4273
4274                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4275                   "    jng_image_interlace_method:  %3d"
4276                   "    jng_alpha_sample_depth:      %3d",
4277                   jng_image_interlace_method,
4278                   jng_alpha_sample_depth);
4279
4280                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4281                   "    jng_alpha_compression_method:%3d\n"
4282                   "    jng_alpha_filter_method:     %3d\n"
4283                   "    jng_alpha_interlace_method:  %3d",
4284                   jng_alpha_compression_method,
4285                   jng_alpha_filter_method,
4286                   jng_alpha_interlace_method);
4287               }
4288           }
4289
4290         if (length != 0)
4291           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4292
4293         continue;
4294       }
4295
4296
4297     if ((reading_idat == MagickFalse) && (read_JSEP == MagickFalse) &&
4298         ((memcmp(type,mng_JDAT,4) == 0) || (memcmp(type,mng_JdAA,4) == 0) ||
4299          (memcmp(type,mng_IDAT,4) == 0) || (memcmp(type,mng_JDAA,4) == 0)))
4300       {
4301         /*
4302            o create color_image
4303            o open color_blob, attached to color_image
4304            o if (color type has alpha)
4305                open alpha_blob, attached to alpha_image
4306         */
4307
4308         color_image_info=(ImageInfo *)AcquireMagickMemory(sizeof(ImageInfo));
4309
4310         if (color_image_info == (ImageInfo *) NULL)
4311           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4312
4313         GetImageInfo(color_image_info);
4314         color_image=AcquireImage(color_image_info,exception);
4315
4316         if (color_image == (Image *) NULL)
4317           ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4318
4319         if (logging != MagickFalse)
4320           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4321             "    Creating color_blob.");
4322
4323         (void) AcquireUniqueFilename(color_image->filename);
4324         status=OpenBlob(color_image_info,color_image,WriteBinaryBlobMode,
4325           exception);
4326
4327         if (status == MagickFalse)
4328           return((Image *) NULL);
4329
4330         if ((image_info->ping == MagickFalse) && (jng_color_type >= 12))
4331           {
4332             alpha_image_info=(ImageInfo *)
4333               AcquireMagickMemory(sizeof(ImageInfo));
4334
4335             if (alpha_image_info == (ImageInfo *) NULL)
4336               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4337
4338             GetImageInfo(alpha_image_info);
4339             alpha_image=AcquireImage(alpha_image_info,exception);
4340
4341             if (alpha_image == (Image *) NULL)
4342               {
4343                 alpha_image=DestroyImage(alpha_image);
4344                 ThrowReaderException(ResourceLimitError,
4345                   "MemoryAllocationFailed");
4346               }
4347
4348             if (logging != MagickFalse)
4349               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4350                 "    Creating alpha_blob.");
4351
4352             (void) AcquireUniqueFilename(alpha_image->filename);
4353             status=OpenBlob(alpha_image_info,alpha_image,WriteBinaryBlobMode,
4354               exception);
4355
4356             if (status == MagickFalse)
4357               return((Image *) NULL);
4358
4359             if (jng_alpha_compression_method == 0)
4360               {
4361                 unsigned char
4362                   data[18];
4363
4364                 if (logging != MagickFalse)
4365                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4366                     "    Writing IHDR chunk to alpha_blob.");
4367
4368                 (void) WriteBlob(alpha_image,8,(const unsigned char *)
4369                   "\211PNG\r\n\032\n");
4370
4371                 (void) WriteBlobMSBULong(alpha_image,13L);
4372                 PNGType(data,mng_IHDR);
4373                 LogPNGChunk(logging,mng_IHDR,13L);
4374                 PNGLong(data+4,jng_width);
4375                 PNGLong(data+8,jng_height);
4376                 data[12]=jng_alpha_sample_depth;
4377                 data[13]=0; /* color_type gray */
4378                 data[14]=0; /* compression method 0 */
4379                 data[15]=0; /* filter_method 0 */
4380                 data[16]=0; /* interlace_method 0 */
4381                 (void) WriteBlob(alpha_image,17,data);
4382                 (void) WriteBlobMSBULong(alpha_image,crc32(0,data,17));
4383               }
4384           }
4385         reading_idat=MagickTrue;
4386       }
4387
4388     if (memcmp(type,mng_JDAT,4) == 0)
4389       {
4390         /* Copy chunk to color_image->blob */
4391
4392         if (logging != MagickFalse)
4393           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4394             "    Copying JDAT chunk data to color_blob.");
4395
4396         (void) WriteBlob(color_image,length,chunk);
4397
4398         if (length != 0)
4399           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4400
4401         continue;
4402       }
4403
4404     if (memcmp(type,mng_IDAT,4) == 0)
4405       {
4406         png_byte
4407            data[5];
4408
4409         /* Copy IDAT header and chunk data to alpha_image->blob */
4410
4411         if (alpha_image != NULL && image_info->ping == MagickFalse)
4412           {
4413             if (logging != MagickFalse)
4414               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4415                 "    Copying IDAT chunk data to alpha_blob.");
4416
4417             (void) WriteBlobMSBULong(alpha_image,(size_t) length);
4418             PNGType(data,mng_IDAT);
4419             LogPNGChunk(logging,mng_IDAT,length);
4420             (void) WriteBlob(alpha_image,4,data);
4421             (void) WriteBlob(alpha_image,length,chunk);
4422             (void) WriteBlobMSBULong(alpha_image,
4423               crc32(crc32(0,data,4),chunk,(uInt) length));
4424           }
4425
4426         if (length != 0)
4427           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4428
4429         continue;
4430       }
4431
4432     if ((memcmp(type,mng_JDAA,4) == 0) || (memcmp(type,mng_JdAA,4) == 0))
4433       {
4434         /* Copy chunk data to alpha_image->blob */
4435
4436         if (alpha_image != NULL && image_info->ping == MagickFalse)
4437           {
4438             if (logging != MagickFalse)
4439               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4440                 "    Copying JDAA chunk data to alpha_blob.");
4441
4442             (void) WriteBlob(alpha_image,length,chunk);
4443           }
4444
4445         if (length != 0)
4446           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4447
4448         continue;
4449       }
4450
4451     if (memcmp(type,mng_JSEP,4) == 0)
4452       {
4453         read_JSEP=MagickTrue;
4454
4455         if (length != 0)
4456           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4457
4458         continue;
4459       }
4460
4461     if (memcmp(type,mng_bKGD,4) == 0)
4462       {
4463         if (length == 2)
4464           {
4465             image->background_color.red=ScaleCharToQuantum(p[1]);
4466             image->background_color.green=image->background_color.red;
4467             image->background_color.blue=image->background_color.red;
4468           }
4469
4470         if (length == 6)
4471           {
4472             image->background_color.red=ScaleCharToQuantum(p[1]);
4473             image->background_color.green=ScaleCharToQuantum(p[3]);
4474             image->background_color.blue=ScaleCharToQuantum(p[5]);
4475           }
4476
4477         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4478         continue;
4479       }
4480
4481     if (memcmp(type,mng_gAMA,4) == 0)
4482       {
4483         if (length == 4)
4484           image->gamma=((float) mng_get_long(p))*0.00001;
4485
4486         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4487         continue;
4488       }
4489
4490     if (memcmp(type,mng_cHRM,4) == 0)
4491       {
4492         if (length == 32)
4493           {
4494             image->chromaticity.white_point.x=0.00001*mng_get_long(p);
4495             image->chromaticity.white_point.y=0.00001*mng_get_long(&p[4]);
4496             image->chromaticity.red_primary.x=0.00001*mng_get_long(&p[8]);
4497             image->chromaticity.red_primary.y=0.00001*mng_get_long(&p[12]);
4498             image->chromaticity.green_primary.x=0.00001*mng_get_long(&p[16]);
4499             image->chromaticity.green_primary.y=0.00001*mng_get_long(&p[20]);
4500             image->chromaticity.blue_primary.x=0.00001*mng_get_long(&p[24]);
4501             image->chromaticity.blue_primary.y=0.00001*mng_get_long(&p[28]);
4502           }
4503
4504         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4505         continue;
4506       }
4507
4508     if (memcmp(type,mng_sRGB,4) == 0)
4509       {
4510         if (length == 1)
4511           {
4512             image->rendering_intent=
4513               Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
4514             image->gamma=1.000f/2.200f;
4515             image->chromaticity.red_primary.x=0.6400f;
4516             image->chromaticity.red_primary.y=0.3300f;
4517             image->chromaticity.green_primary.x=0.3000f;
4518             image->chromaticity.green_primary.y=0.6000f;
4519             image->chromaticity.blue_primary.x=0.1500f;
4520             image->chromaticity.blue_primary.y=0.0600f;
4521             image->chromaticity.white_point.x=0.3127f;
4522             image->chromaticity.white_point.y=0.3290f;
4523           }
4524
4525         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4526         continue;
4527       }
4528
4529     if (memcmp(type,mng_oFFs,4) == 0)
4530       {
4531         if (length > 8)
4532           {
4533             image->page.x=(ssize_t) mng_get_long(p);
4534             image->page.y=(ssize_t) mng_get_long(&p[4]);
4535
4536             if ((int) p[8] != 0)
4537               {
4538                 image->page.x/=10000;
4539                 image->page.y/=10000;
4540               }
4541           }
4542
4543         if (length != 0)
4544           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4545
4546         continue;
4547       }
4548
4549     if (memcmp(type,mng_pHYs,4) == 0)
4550       {
4551         if (length > 8)
4552           {
4553             image->resolution.x=(double) mng_get_long(p);
4554             image->resolution.y=(double) mng_get_long(&p[4]);
4555             if ((int) p[8] == PNG_RESOLUTION_METER)
4556               {
4557                 image->units=PixelsPerCentimeterResolution;
4558                 image->resolution.x=image->resolution.x/100.0f;
4559                 image->resolution.y=image->resolution.y/100.0f;
4560               }
4561           }
4562
4563         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4564         continue;
4565       }
4566
4567 #if 0
4568     if (memcmp(type,mng_iCCP,4) == 0)
4569       {
4570         /* To do: */
4571         if (length != 0)
4572           chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4573
4574         continue;
4575       }
4576 #endif
4577
4578     if (length != 0)
4579       chunk=(unsigned char *) RelinquishMagickMemory(chunk);
4580
4581     if (memcmp(type,mng_IEND,4))
4582       continue;
4583
4584     break;
4585   }
4586
4587
4588   /* IEND found */
4589
4590   /*
4591     Finish up reading image data:
4592
4593        o read main image from color_blob.
4594
4595        o close color_blob.
4596
4597        o if (color_type has alpha)
4598             if alpha_encoding is PNG
4599                read secondary image from alpha_blob via ReadPNG
4600             if alpha_encoding is JPEG
4601                read secondary image from alpha_blob via ReadJPEG
4602
4603        o close alpha_blob.
4604
4605        o copy intensity of secondary image into
4606          alpha samples of main image.
4607
4608        o destroy the secondary image.
4609   */
4610
4611   if (color_image_info == (ImageInfo *) NULL)
4612     {
4613       assert(color_image == (Image *) NULL);
4614       assert(alpha_image == (Image *) NULL);
4615       return((Image *) NULL);
4616     }
4617
4618   if (color_image == (Image *) NULL)
4619     {
4620       assert(alpha_image == (Image *) NULL);
4621       return((Image *) NULL);
4622     }
4623
4624   (void) SeekBlob(color_image,0,SEEK_SET);
4625
4626   if (logging != MagickFalse)
4627     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4628       "    Reading jng_image from color_blob.");
4629
4630   assert(color_image_info != (ImageInfo *) NULL);
4631   (void) FormatLocaleString(color_image_info->filename,MagickPathExtent,"%s",
4632     color_image->filename);
4633
4634   color_image_info->ping=MagickFalse;   /* To do: avoid this */
4635   jng_image=ReadImage(color_image_info,exception);
4636
4637   (void) RelinquishUniqueFileResource(color_image->filename);
4638   color_image=DestroyImage(color_image);
4639   color_image_info=DestroyImageInfo(color_image_info);
4640
4641   if (jng_image == (Image *) NULL)
4642     return((Image *) NULL);
4643
4644   if (logging != MagickFalse)
4645     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4646       "    Copying jng_image pixels to main image.");
4647
4648   image->rows=jng_height;
4649   image->columns=jng_width;
4650
4651   status=SetImageExtent(image,image->columns,image->rows,exception);
4652   if (status == MagickFalse)
4653     return(DestroyImageList(image));
4654
4655   for (y=0; y < (ssize_t) image->rows; y++)
4656   {
4657     s=GetVirtualPixels(jng_image,0,y,image->columns,1,exception);
4658     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4659     for (x=(ssize_t) image->columns; x != 0; x--)
4660     {
4661       SetPixelRed(image,GetPixelRed(jng_image,s),q);
4662       SetPixelGreen(image,GetPixelGreen(jng_image,s),q);
4663       SetPixelBlue(image,GetPixelBlue(jng_image,s),q);
4664       q+=GetPixelChannels(image);
4665       s+=GetPixelChannels(jng_image);
4666     }
4667
4668     if (SyncAuthenticPixels(image,exception) == MagickFalse)
4669       break;
4670   }
4671
4672   jng_image=DestroyImage(jng_image);
4673
4674   if (image_info->ping == MagickFalse)
4675     {
4676      if (jng_color_type >= 12)
4677        {
4678          if (jng_alpha_compression_method == 0)
4679            {
4680              png_byte
4681                data[5];
4682              (void) WriteBlobMSBULong(alpha_image,0x00000000L);
4683              PNGType(data,mng_IEND);
4684              LogPNGChunk(logging,mng_IEND,0L);
4685              (void) WriteBlob(alpha_image,4,data);
4686              (void) WriteBlobMSBULong(alpha_image,crc32(0,data,4));
4687            }
4688
4689          (void) CloseBlob(alpha_image);
4690
4691          if (logging != MagickFalse)
4692            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4693              "    Reading alpha from alpha_blob.");
4694
4695          (void) FormatLocaleString(alpha_image_info->filename,MagickPathExtent,
4696            "%s",alpha_image->filename);
4697
4698          jng_image=ReadImage(alpha_image_info,exception);
4699
4700          if (jng_image != (Image *) NULL)
4701            for (y=0; y < (ssize_t) image->rows; y++)
4702            {
4703              s=GetVirtualPixels(jng_image,0,y,image->columns,1,
4704                exception);
4705              q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4706
4707              if (image->alpha_trait != UndefinedPixelTrait)
4708                for (x=(ssize_t) image->columns; x != 0; x--)
4709                {
4710                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4711                   q+=GetPixelChannels(image);
4712                   s+=GetPixelChannels(jng_image);
4713                }
4714
4715              else
4716                for (x=(ssize_t) image->columns; x != 0; x--)
4717                {
4718                   SetPixelAlpha(image,GetPixelRed(jng_image,s),q);
4719                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
4720                     image->alpha_trait=BlendPixelTrait;
4721                   q+=GetPixelChannels(image);
4722                   s+=GetPixelChannels(jng_image);
4723                }
4724
4725              if (SyncAuthenticPixels(image,exception) == MagickFalse)
4726                break;
4727            }
4728          (void) RelinquishUniqueFileResource(alpha_image->filename);
4729          alpha_image=DestroyImage(alpha_image);
4730          alpha_image_info=DestroyImageInfo(alpha_image_info);
4731          if (jng_image != (Image *) NULL)
4732            jng_image=DestroyImage(jng_image);
4733        }
4734     }
4735
4736   /* Read the JNG image.  */
4737
4738   if (mng_info->mng_type == 0)
4739     {
4740       mng_info->mng_width=jng_width;
4741       mng_info->mng_height=jng_height;
4742     }
4743
4744   if (image->page.width == 0 && image->page.height == 0)
4745     {
4746       image->page.width=jng_width;
4747       image->page.height=jng_height;
4748     }
4749
4750   if (image->page.x == 0 && image->page.y == 0)
4751     {
4752       image->page.x=mng_info->x_off[mng_info->object_id];
4753       image->page.y=mng_info->y_off[mng_info->object_id];
4754     }
4755
4756   else
4757     {
4758       image->page.y=mng_info->y_off[mng_info->object_id];
4759     }
4760
4761   mng_info->image_found++;
4762   status=SetImageProgress(image,LoadImagesTag,2*TellBlob(image),
4763     2*GetBlobSize(image));
4764
4765   if (status == MagickFalse)
4766     return((Image *) NULL);
4767
4768   if (logging != MagickFalse)
4769     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4770       "  exit ReadOneJNGImage()");
4771
4772   return(image);
4773 }
4774
4775 /*
4776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4777 %                                                                             %
4778 %                                                                             %
4779 %                                                                             %
4780 %   R e a d J N G I m a g e                                                   %
4781 %                                                                             %
4782 %                                                                             %
4783 %                                                                             %
4784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4785 %
4786 %  ReadJNGImage() reads a JPEG Network Graphics (JNG) image file
4787 %  (including the 8-byte signature)  and returns it.  It allocates the memory
4788 %  necessary for the new Image structure and returns a pointer to the new
4789 %  image.
4790 %
4791 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
4792 %
4793 %  The format of the ReadJNGImage method is:
4794 %
4795 %      Image *ReadJNGImage(const ImageInfo *image_info, ExceptionInfo
4796 %         *exception)
4797 %
4798 %  A description of each parameter follows:
4799 %
4800 %    o image_info: the image info.
4801 %
4802 %    o exception: return any errors or warnings in this structure.
4803 %
4804 */
4805
4806 static Image *ReadJNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4807 {
4808   Image
4809     *image;
4810
4811   MagickBooleanType
4812     have_mng_structure,
4813     logging,
4814     status;
4815
4816   MngInfo
4817     *mng_info;
4818
4819   char
4820     magic_number[MagickPathExtent];
4821
4822   size_t
4823     count;
4824
4825   /*
4826     Open image file.
4827   */
4828   assert(image_info != (const ImageInfo *) NULL);
4829   assert(image_info->signature == MagickCoreSignature);
4830   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4831   assert(exception != (ExceptionInfo *) NULL);
4832   assert(exception->signature == MagickCoreSignature);
4833   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadJNGImage()");
4834   image=AcquireImage(image_info,exception);
4835   mng_info=(MngInfo *) NULL;
4836   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
4837
4838   if (status == MagickFalse)
4839     return((Image *) NULL);
4840
4841   if (LocaleCompare(image_info->magick,"JNG") != 0)
4842     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4843
4844   /* Verify JNG signature.  */
4845
4846   count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
4847
4848   if (count < 8 || memcmp(magic_number,"\213JNG\r\n\032\n",8) != 0)
4849     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
4850
4851   /* Allocate a MngInfo structure.  */
4852
4853   have_mng_structure=MagickFalse;
4854   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(*mng_info));
4855
4856   if (mng_info == (MngInfo *) NULL)
4857     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
4858
4859   /* Initialize members of the MngInfo structure.  */
4860
4861   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
4862   have_mng_structure=MagickTrue;
4863
4864   mng_info->image=image;
4865   image=ReadOneJNGImage(mng_info,image_info,exception);
4866   MngInfoFreeStruct(mng_info,&have_mng_structure);
4867
4868   if (image == (Image *) NULL)
4869     {
4870       if (logging != MagickFalse)
4871         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4872           "exit ReadJNGImage() with error");
4873
4874       return((Image *) NULL);
4875     }
4876   (void) CloseBlob(image);
4877
4878   if (image->columns == 0 || image->rows == 0)
4879     {
4880       if (logging != MagickFalse)
4881         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
4882           "exit ReadJNGImage() with error");
4883
4884       ThrowReaderException(CorruptImageError,"CorruptImage");
4885     }
4886
4887   if (logging != MagickFalse)
4888     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadJNGImage()");
4889
4890   return(image);
4891 }
4892 #endif
4893
4894 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
4895 {
4896   char
4897     page_geometry[MagickPathExtent];
4898
4899   Image
4900     *image;
4901
4902   MagickBooleanType
4903     logging,
4904     have_mng_structure;
4905
4906   volatile int
4907     first_mng_object,
4908     object_id,
4909     term_chunk_found,
4910     skip_to_iend;
4911
4912   volatile ssize_t
4913     image_count=0;
4914
4915   MagickBooleanType
4916     status;
4917
4918   MagickOffsetType
4919     offset;
4920
4921   MngInfo
4922     *mng_info;
4923
4924   MngBox
4925     default_fb,
4926     fb,
4927     previous_fb;
4928
4929 #if defined(MNG_INSERT_LAYERS)
4930   PixelInfo
4931     mng_background_color;
4932 #endif
4933
4934   register unsigned char
4935     *p;
4936
4937   register ssize_t
4938     i;
4939
4940   size_t
4941     count;
4942
4943   ssize_t
4944     loop_level;
4945
4946   volatile short
4947     skipping_loop;
4948
4949 #if defined(MNG_INSERT_LAYERS)
4950   unsigned int
4951     mandatory_back=0;
4952 #endif
4953
4954   volatile unsigned int
4955 #ifdef MNG_OBJECT_BUFFERS
4956     mng_background_object=0,
4957 #endif
4958     mng_type=0;   /* 0: PNG or JNG; 1: MNG; 2: MNG-LC; 3: MNG-VLC */
4959
4960   size_t
4961     default_frame_timeout,
4962     frame_timeout,
4963 #if defined(MNG_INSERT_LAYERS)
4964     image_height,
4965     image_width,
4966 #endif
4967     length;
4968
4969   /* These delays are all measured in image ticks_per_second,
4970    * not in MNG ticks_per_second
4971    */
4972   volatile size_t
4973     default_frame_delay,
4974     final_delay,
4975     final_image_delay,
4976     frame_delay,
4977 #if defined(MNG_INSERT_LAYERS)
4978     insert_layers,
4979 #endif
4980     mng_iterations=1,
4981     simplicity=0,
4982     subframe_height=0,
4983     subframe_width=0;
4984
4985   previous_fb.top=0;
4986   previous_fb.bottom=0;
4987   previous_fb.left=0;
4988   previous_fb.right=0;
4989   default_fb.top=0;
4990   default_fb.bottom=0;
4991   default_fb.left=0;
4992   default_fb.right=0;
4993
4994   /* Open image file.  */
4995
4996   assert(image_info != (const ImageInfo *) NULL);
4997   assert(image_info->signature == MagickCoreSignature);
4998   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image_info->filename);
4999   assert(exception != (ExceptionInfo *) NULL);
5000   assert(exception->signature == MagickCoreSignature);
5001   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter ReadMNGImage()");
5002   image=AcquireImage(image_info,exception);
5003   mng_info=(MngInfo *) NULL;
5004   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
5005
5006   if (status == MagickFalse)
5007     return((Image *) NULL);
5008
5009   first_mng_object=MagickFalse;
5010   skipping_loop=(-1);
5011   have_mng_structure=MagickFalse;
5012
5013   /* Allocate a MngInfo structure.  */
5014
5015   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
5016
5017   if (mng_info == (MngInfo *) NULL)
5018     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5019
5020   /* Initialize members of the MngInfo structure.  */
5021
5022   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
5023   mng_info->image=image;
5024   have_mng_structure=MagickTrue;
5025
5026   if (LocaleCompare(image_info->magick,"MNG") == 0)
5027     {
5028       char
5029         magic_number[MagickPathExtent];
5030
5031       /* Verify MNG signature.  */
5032       count=(size_t) ReadBlob(image,8,(unsigned char *) magic_number);
5033       if (memcmp(magic_number,"\212MNG\r\n\032\n",8) != 0)
5034         ThrowReaderException(CorruptImageError,"ImproperImageHeader");
5035
5036       /* Initialize some nonzero members of the MngInfo structure.  */
5037       for (i=0; i < MNG_MAX_OBJECTS; i++)
5038       {
5039         mng_info->object_clip[i].right=(ssize_t) PNG_UINT_31_MAX;
5040         mng_info->object_clip[i].bottom=(ssize_t) PNG_UINT_31_MAX;
5041       }
5042       mng_info->exists[0]=MagickTrue;
5043     }
5044
5045   first_mng_object=MagickTrue;
5046   mng_type=0;
5047 #if defined(MNG_INSERT_LAYERS)
5048   insert_layers=MagickFalse; /* should be False when converting or mogrifying */
5049 #endif
5050   default_frame_delay=0;
5051   default_frame_timeout=0;
5052   frame_delay=0;
5053   final_delay=1;
5054   mng_info->ticks_per_second=1UL*image->ticks_per_second;
5055   object_id=0;
5056   skip_to_iend=MagickFalse;
5057   term_chunk_found=MagickFalse;
5058   mng_info->framing_mode=1;
5059 #if defined(MNG_INSERT_LAYERS)
5060   mandatory_back=MagickFalse;
5061 #endif
5062 #if defined(MNG_INSERT_LAYERS)
5063   mng_background_color=image->background_color;
5064 #endif
5065   default_fb=mng_info->frame;
5066   previous_fb=mng_info->frame;
5067   do
5068   {
5069     char
5070       type[MagickPathExtent];
5071
5072     if (LocaleCompare(image_info->magick,"MNG") == 0)
5073       {
5074         unsigned char
5075           *chunk;
5076
5077         /*
5078           Read a new chunk.
5079         */
5080         type[0]='\0';
5081         (void) ConcatenateMagickString(type,"errr",MagickPathExtent);
5082         length=ReadBlobMSBLong(image);
5083         count=(size_t) ReadBlob(image,4,(unsigned char *) type);
5084
5085         if (logging != MagickFalse)
5086           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5087            "  Reading MNG chunk type %c%c%c%c, length: %.20g",
5088            type[0],type[1],type[2],type[3],(double) length);
5089
5090         if (length > PNG_UINT_31_MAX)
5091           {
5092             status=MagickFalse;
5093             break;
5094           }
5095
5096         if (count == 0)
5097           ThrowReaderException(CorruptImageError,"CorruptImage");
5098
5099         p=NULL;
5100         chunk=(unsigned char *) NULL;
5101
5102         if (length != 0)
5103           {
5104             chunk=(unsigned char *) AcquireQuantumMemory(length,sizeof(*chunk));
5105
5106             if (chunk == (unsigned char *) NULL)
5107               ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
5108
5109             for (i=0; i < (ssize_t) length; i++)
5110               chunk[i]=(unsigned char) ReadBlobByte(image);
5111
5112             p=chunk;
5113           }
5114
5115         (void) ReadBlobMSBLong(image);  /* read crc word */
5116
5117 #if !defined(JNG_SUPPORTED)
5118         if (memcmp(type,mng_JHDR,4) == 0)
5119           {
5120             skip_to_iend=MagickTrue;
5121
5122             if (mng_info->jhdr_warning == 0)
5123               (void) ThrowMagickException(exception,GetMagickModule(),
5124                 CoderError,"JNGCompressNotSupported","`%s'",image->filename);
5125
5126             mng_info->jhdr_warning++;
5127           }
5128 #endif
5129         if (memcmp(type,mng_DHDR,4) == 0)
5130           {
5131             skip_to_iend=MagickTrue;
5132
5133             if (mng_info->dhdr_warning == 0)
5134               (void) ThrowMagickException(exception,GetMagickModule(),
5135                 CoderError,"DeltaPNGNotSupported","`%s'",image->filename);
5136
5137             mng_info->dhdr_warning++;
5138           }
5139         if (memcmp(type,mng_MEND,4) == 0)
5140           break;
5141
5142         if (skip_to_iend)
5143           {
5144             if (memcmp(type,mng_IEND,4) == 0)
5145               skip_to_iend=MagickFalse;
5146
5147             if (length != 0)
5148               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5149
5150             if (logging != MagickFalse)
5151               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5152                 "  Skip to IEND.");
5153
5154             continue;
5155           }
5156
5157         if (memcmp(type,mng_MHDR,4) == 0)
5158           {
5159             if (length != 28)
5160               {
5161                 if (chunk)
5162                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5163                 ThrowReaderException(CorruptImageError,"CorruptImage");
5164               }
5165
5166             mng_info->mng_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
5167                 (p[2] << 8) | p[3]);
5168
5169             mng_info->mng_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
5170                 (p[6] << 8) | p[7]);
5171
5172             if (logging != MagickFalse)
5173               {
5174                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5175                   "  MNG width: %.20g",(double) mng_info->mng_width);
5176                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5177                   "  MNG height: %.20g",(double) mng_info->mng_height);
5178               }
5179
5180             p+=8;
5181             mng_info->ticks_per_second=(size_t) mng_get_long(p);
5182
5183             if (mng_info->ticks_per_second == 0)
5184               default_frame_delay=0;
5185
5186             else
5187               default_frame_delay=1UL*image->ticks_per_second/
5188                 mng_info->ticks_per_second;
5189
5190             frame_delay=default_frame_delay;
5191             simplicity=0;
5192
5193             p+=16;
5194             simplicity=(size_t) mng_get_long(p);
5195
5196             mng_type=1;    /* Full MNG */
5197
5198             if ((simplicity != 0) && ((simplicity | 11) == 11))
5199               mng_type=2; /* LC */
5200
5201             if ((simplicity != 0) && ((simplicity | 9) == 9))
5202               mng_type=3; /* VLC */
5203
5204 #if defined(MNG_INSERT_LAYERS)
5205             if (mng_type != 3)
5206               insert_layers=MagickTrue;
5207 #endif
5208             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5209               {
5210                 /* Allocate next image structure.  */
5211                 AcquireNextImage(image_info,image,exception);
5212
5213                 if (GetNextImageInList(image) == (Image *) NULL)
5214                   return((Image *) NULL);
5215
5216                 image=SyncNextImageInList(image);
5217                 mng_info->image=image;
5218               }
5219
5220             if ((mng_info->mng_width > 65535L) ||
5221                 (mng_info->mng_height > 65535L))
5222               ThrowReaderException(ImageError,"WidthOrHeightExceedsLimit");
5223
5224             (void) FormatLocaleString(page_geometry,MagickPathExtent,
5225               "%.20gx%.20g+0+0",(double) mng_info->mng_width,(double)
5226               mng_info->mng_height);
5227
5228             mng_info->frame.left=0;
5229             mng_info->frame.right=(ssize_t) mng_info->mng_width;
5230             mng_info->frame.top=0;
5231             mng_info->frame.bottom=(ssize_t) mng_info->mng_height;
5232             mng_info->clip=default_fb=previous_fb=mng_info->frame;
5233
5234             for (i=0; i < MNG_MAX_OBJECTS; i++)
5235               mng_info->object_clip[i]=mng_info->frame;
5236
5237             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5238             continue;
5239           }
5240
5241         if (memcmp(type,mng_TERM,4) == 0)
5242           {
5243             int
5244               repeat=0;
5245
5246             if (length != 0)
5247               repeat=p[0];
5248
5249             if (repeat == 3)
5250               {
5251                 final_delay=(png_uint_32) mng_get_long(&p[2]);
5252                 mng_iterations=(png_uint_32) mng_get_long(&p[6]);
5253
5254                 if (mng_iterations == PNG_UINT_31_MAX)
5255                   mng_iterations=0;
5256
5257                 image->iterations=mng_iterations;
5258                 term_chunk_found=MagickTrue;
5259               }
5260
5261             if (logging != MagickFalse)
5262               {
5263                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5264                   "    repeat=%d,  final_delay=%.20g,  iterations=%.20g",
5265                   repeat,(double) final_delay, (double) image->iterations);
5266               }
5267
5268             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5269             continue;
5270           }
5271         if (memcmp(type,mng_DEFI,4) == 0)
5272           {
5273             if (mng_type == 3)
5274               (void) ThrowMagickException(exception,GetMagickModule(),
5275                 CoderError,"DEFI chunk found in MNG-VLC datastream","`%s'",
5276                 image->filename);
5277
5278             if (length < 2)
5279               {
5280                 if (chunk)
5281                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5282                 ThrowReaderException(CorruptImageError,"CorruptImage");
5283               }
5284
5285             object_id=(p[0] << 8) | p[1];
5286
5287             if (mng_type == 2 && object_id != 0)
5288               (void) ThrowMagickException(exception,GetMagickModule(),
5289                 CoderError,"Nonzero object_id in MNG-LC datastream","`%s'",
5290                 image->filename);
5291
5292             if (object_id > MNG_MAX_OBJECTS)
5293               {
5294                 /*
5295                   Instead of using a warning we should allocate a larger
5296                   MngInfo structure and continue.
5297                 */
5298                 (void) ThrowMagickException(exception,GetMagickModule(),
5299                   CoderError,"object id too large","`%s'",image->filename);
5300                 object_id=MNG_MAX_OBJECTS;
5301               }
5302
5303             if (mng_info->exists[object_id])
5304               if (mng_info->frozen[object_id])
5305                 {
5306                   chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5307                   (void) ThrowMagickException(exception,
5308                     GetMagickModule(),CoderError,
5309                     "DEFI cannot redefine a frozen MNG object","`%s'",
5310                     image->filename);
5311                   continue;
5312                 }
5313
5314             mng_info->exists[object_id]=MagickTrue;
5315
5316             if (length > 2)
5317               mng_info->invisible[object_id]=p[2];
5318
5319             /*
5320               Extract object offset info.
5321             */
5322             if (length > 11)
5323               {
5324                 mng_info->x_off[object_id]=(ssize_t) ((p[4] << 24) |
5325                     (p[5] << 16) | (p[6] << 8) | p[7]);
5326
5327                 mng_info->y_off[object_id]=(ssize_t) ((p[8] << 24) |
5328                     (p[9] << 16) | (p[10] << 8) | p[11]);
5329
5330                 if (logging != MagickFalse)
5331                   {
5332                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5333                       "  x_off[%d]: %.20g,  y_off[%d]: %.20g",
5334                       object_id,(double) mng_info->x_off[object_id],
5335                       object_id,(double) mng_info->y_off[object_id]);
5336                   }
5337               }
5338
5339             /*
5340               Extract object clipping info.
5341             */
5342             if (length > 27)
5343               mng_info->object_clip[object_id]=mng_read_box(mng_info->frame,0,
5344                 &p[12]);
5345
5346             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5347             continue;
5348           }
5349         if (memcmp(type,mng_bKGD,4) == 0)
5350           {
5351             mng_info->have_global_bkgd=MagickFalse;
5352
5353             if (length > 5)
5354               {
5355                 mng_info->mng_global_bkgd.red=
5356                   ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5357
5358                 mng_info->mng_global_bkgd.green=
5359                   ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5360
5361                 mng_info->mng_global_bkgd.blue=
5362                   ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5363
5364                 mng_info->have_global_bkgd=MagickTrue;
5365               }
5366
5367             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5368             continue;
5369           }
5370         if (memcmp(type,mng_BACK,4) == 0)
5371           {
5372 #if defined(MNG_INSERT_LAYERS)
5373             if (length > 6)
5374               mandatory_back=p[6];
5375
5376             else
5377               mandatory_back=0;
5378
5379             if (mandatory_back && length > 5)
5380               {
5381                 mng_background_color.red=
5382                     ScaleShortToQuantum((unsigned short) ((p[0] << 8) | p[1]));
5383
5384                 mng_background_color.green=
5385                     ScaleShortToQuantum((unsigned short) ((p[2] << 8) | p[3]));
5386
5387                 mng_background_color.blue=
5388                     ScaleShortToQuantum((unsigned short) ((p[4] << 8) | p[5]));
5389
5390                 mng_background_color.alpha=OpaqueAlpha;
5391               }
5392
5393 #ifdef MNG_OBJECT_BUFFERS
5394             if (length > 8)
5395               mng_background_object=(p[7] << 8) | p[8];
5396 #endif
5397 #endif
5398             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5399             continue;
5400           }
5401
5402         if (memcmp(type,mng_PLTE,4) == 0)
5403           {
5404             /* Read global PLTE.  */
5405
5406             if (length && (length < 769))
5407               {
5408                 if (mng_info->global_plte == (png_colorp) NULL)
5409                   mng_info->global_plte=(png_colorp) AcquireQuantumMemory(256,
5410                     sizeof(*mng_info->global_plte));
5411
5412                 for (i=0; i < (ssize_t) (length/3); i++)
5413                 {
5414                   mng_info->global_plte[i].red=p[3*i];
5415                   mng_info->global_plte[i].green=p[3*i+1];
5416                   mng_info->global_plte[i].blue=p[3*i+2];
5417                 }
5418
5419                 mng_info->global_plte_length=(unsigned int) (length/3);
5420               }
5421 #ifdef MNG_LOOSE
5422             for ( ; i < 256; i++)
5423             {
5424               mng_info->global_plte[i].red=i;
5425               mng_info->global_plte[i].green=i;
5426               mng_info->global_plte[i].blue=i;
5427             }
5428
5429             if (length != 0)
5430               mng_info->global_plte_length=256;
5431 #endif
5432             else
5433               mng_info->global_plte_length=0;
5434
5435             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5436             continue;
5437           }
5438
5439         if (memcmp(type,mng_tRNS,4) == 0)
5440           {
5441             /* read global tRNS */
5442
5443             if (length > 0 && length < 257)
5444               for (i=0; i < (ssize_t) length; i++)
5445                 mng_info->global_trns[i]=p[i];
5446
5447 #ifdef MNG_LOOSE
5448             for ( ; i < 256; i++)
5449               mng_info->global_trns[i]=255;
5450 #endif
5451             mng_info->global_trns_length=(unsigned int) length;
5452             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5453             continue;
5454           }
5455         if (memcmp(type,mng_gAMA,4) == 0)
5456           {
5457             if (length == 4)
5458               {
5459                 ssize_t
5460                   igamma;
5461
5462                 igamma=mng_get_long(p);
5463                 mng_info->global_gamma=((float) igamma)*0.00001;
5464                 mng_info->have_global_gama=MagickTrue;
5465               }
5466
5467             else
5468               mng_info->have_global_gama=MagickFalse;
5469
5470             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5471             continue;
5472           }
5473
5474         if (memcmp(type,mng_cHRM,4) == 0)
5475           {
5476             /* Read global cHRM */
5477
5478             if (length == 32)
5479               {
5480                 mng_info->global_chrm.white_point.x=0.00001*mng_get_long(p);
5481                 mng_info->global_chrm.white_point.y=0.00001*mng_get_long(&p[4]);
5482                 mng_info->global_chrm.red_primary.x=0.00001*mng_get_long(&p[8]);
5483                 mng_info->global_chrm.red_primary.y=0.00001*
5484                   mng_get_long(&p[12]);
5485                 mng_info->global_chrm.green_primary.x=0.00001*
5486                   mng_get_long(&p[16]);
5487                 mng_info->global_chrm.green_primary.y=0.00001*
5488                   mng_get_long(&p[20]);
5489                 mng_info->global_chrm.blue_primary.x=0.00001*
5490                   mng_get_long(&p[24]);
5491                 mng_info->global_chrm.blue_primary.y=0.00001*
5492                   mng_get_long(&p[28]);
5493                 mng_info->have_global_chrm=MagickTrue;
5494               }
5495             else
5496               mng_info->have_global_chrm=MagickFalse;
5497
5498             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5499             continue;
5500           }
5501
5502         if (memcmp(type,mng_sRGB,4) == 0)
5503           {
5504             /*
5505               Read global sRGB.
5506             */
5507             if (length != 0)
5508               {
5509                 mng_info->global_srgb_intent=
5510                   Magick_RenderingIntent_from_PNG_RenderingIntent(p[0]);
5511                 mng_info->have_global_srgb=MagickTrue;
5512               }
5513             else
5514               mng_info->have_global_srgb=MagickFalse;
5515
5516             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5517             continue;
5518           }
5519
5520         if (memcmp(type,mng_iCCP,4) == 0)
5521           {
5522             /* To do: */
5523
5524             /*
5525               Read global iCCP.
5526             */
5527             if (length != 0)
5528               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5529
5530             continue;
5531           }
5532
5533         if (memcmp(type,mng_FRAM,4) == 0)
5534           {
5535             if (mng_type == 3)
5536               (void) ThrowMagickException(exception,GetMagickModule(),
5537                 CoderError,"FRAM chunk found in MNG-VLC datastream","`%s'",
5538                 image->filename);
5539
5540             if ((mng_info->framing_mode == 2) || (mng_info->framing_mode == 4))
5541               image->delay=frame_delay;
5542
5543             frame_delay=default_frame_delay;
5544             frame_timeout=default_frame_timeout;
5545             fb=default_fb;
5546
5547             if (length != 0)
5548               if (p[0])
5549                 mng_info->framing_mode=p[0];
5550
5551             if (logging != MagickFalse)
5552               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5553                 "    Framing_mode=%d",mng_info->framing_mode);
5554
5555             if (length > 6)
5556               {
5557                 /* Note the delay and frame clipping boundaries.  */
5558
5559                 p++; /* framing mode */
5560
5561                 while (*p && ((p-chunk) < (ssize_t) length))
5562                   p++;  /* frame name */
5563
5564                 p++;  /* frame name terminator */
5565
5566                 if ((p-chunk) < (ssize_t) (length-4))
5567                   {
5568                     int
5569                       change_delay,
5570                       change_timeout,
5571                       change_clipping;
5572
5573                     change_delay=(*p++);
5574                     change_timeout=(*p++);
5575                     change_clipping=(*p++);
5576                     p++; /* change_sync */
5577
5578                     if (change_delay)
5579                       {
5580                         frame_delay=1UL*image->ticks_per_second*
5581                           mng_get_long(p);
5582
5583                         if (mng_info->ticks_per_second != 0)
5584                           frame_delay/=mng_info->ticks_per_second;
5585
5586                         else
5587                           frame_delay=PNG_UINT_31_MAX;
5588
5589                         if (change_delay == 2)
5590                           default_frame_delay=frame_delay;
5591
5592                         p+=4;
5593
5594                         if (logging != MagickFalse)
5595                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5596                             "    Framing_delay=%.20g",(double) frame_delay);
5597                       }
5598
5599                     if (change_timeout)
5600                       {
5601                         frame_timeout=1UL*image->ticks_per_second*
5602                           mng_get_long(p);
5603
5604                         if (mng_info->ticks_per_second != 0)
5605                           frame_timeout/=mng_info->ticks_per_second;
5606
5607                         else
5608                           frame_timeout=PNG_UINT_31_MAX;
5609
5610                         if (change_timeout == 2)
5611                           default_frame_timeout=frame_timeout;
5612
5613                         p+=4;
5614
5615                         if (logging != MagickFalse)
5616                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5617                             "    Framing_timeout=%.20g",(double) frame_timeout);
5618                       }
5619
5620                     if (change_clipping)
5621                       {
5622                         fb=mng_read_box(previous_fb,(char) p[0],&p[1]);
5623                         p+=17;
5624                         previous_fb=fb;
5625
5626                         if (logging != MagickFalse)
5627                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5628                             "    Frame_clip: L=%.20g R=%.20g T=%.20g B=%.20g",
5629                             (double) fb.left,(double) fb.right,(double) fb.top,
5630                             (double) fb.bottom);
5631
5632                         if (change_clipping == 2)
5633                           default_fb=fb;
5634                       }
5635                   }
5636               }
5637             mng_info->clip=fb;
5638             mng_info->clip=mng_minimum_box(fb,mng_info->frame);
5639
5640             subframe_width=(size_t) (mng_info->clip.right
5641                -mng_info->clip.left);
5642
5643             subframe_height=(size_t) (mng_info->clip.bottom
5644                -mng_info->clip.top);
5645             /*
5646               Insert a background layer behind the frame if framing_mode is 4.
5647             */
5648 #if defined(MNG_INSERT_LAYERS)
5649             if (logging != MagickFalse)
5650               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5651                 "   subframe_width=%.20g, subframe_height=%.20g",(double)
5652                 subframe_width,(double) subframe_height);
5653
5654             if (insert_layers && (mng_info->framing_mode == 4) &&
5655                 (subframe_width) && (subframe_height))
5656               {
5657                 /* Allocate next image structure.  */
5658                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
5659                   {
5660                     AcquireNextImage(image_info,image,exception);
5661
5662                     if (GetNextImageInList(image) == (Image *) NULL)
5663                       {
5664                         image=DestroyImageList(image);
5665                         MngInfoFreeStruct(mng_info,&have_mng_structure);
5666                         return((Image *) NULL);
5667                       }
5668
5669                     image=SyncNextImageInList(image);
5670                   }
5671
5672                 mng_info->image=image;
5673
5674                 if (term_chunk_found)
5675                   {
5676                     image->start_loop=MagickTrue;
5677                     image->iterations=mng_iterations;
5678                     term_chunk_found=MagickFalse;
5679                   }
5680
5681                 else
5682                     image->start_loop=MagickFalse;
5683
5684                 image->columns=subframe_width;
5685                 image->rows=subframe_height;
5686                 image->page.width=subframe_width;
5687                 image->page.height=subframe_height;
5688                 image->page.x=mng_info->clip.left;
5689                 image->page.y=mng_info->clip.top;
5690                 image->background_color=mng_background_color;
5691                 image->alpha_trait=UndefinedPixelTrait;
5692                 image->delay=0;
5693                 (void) SetImageBackgroundColor(image,exception);
5694
5695                 if (logging != MagickFalse)
5696                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5697                     "  Insert backgd layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
5698                     (double) mng_info->clip.left,(double) mng_info->clip.right,
5699                     (double) mng_info->clip.top,(double) mng_info->clip.bottom);
5700               }
5701 #endif
5702             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5703             continue;
5704           }
5705
5706         if (memcmp(type,mng_CLIP,4) == 0)
5707           {
5708             unsigned int
5709               first_object,
5710               last_object;
5711
5712             /*
5713               Read CLIP.
5714             */
5715             if (length > 3)
5716               {
5717                 first_object=(p[0] << 8) | p[1];
5718                 last_object=(p[2] << 8) | p[3];
5719                 p+=4;
5720
5721                 for (i=(int) first_object; i <= (int) last_object; i++)
5722                 {
5723                   if (mng_info->exists[i] && !mng_info->frozen[i])
5724                     {
5725                       MngBox
5726                         box;
5727
5728                       box=mng_info->object_clip[i];
5729                       if ((p-chunk) < (ssize_t) (length-17))
5730                         mng_info->object_clip[i]=
5731                            mng_read_box(box,(char) p[0],&p[1]);
5732                     }
5733                 }
5734
5735               }
5736             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5737             continue;
5738           }
5739
5740         if (memcmp(type,mng_SAVE,4) == 0)
5741           {
5742             for (i=1; i < MNG_MAX_OBJECTS; i++)
5743               if (mng_info->exists[i])
5744                 {
5745                  mng_info->frozen[i]=MagickTrue;
5746 #ifdef MNG_OBJECT_BUFFERS
5747                  if (mng_info->ob[i] != (MngBuffer *) NULL)
5748                     mng_info->ob[i]->frozen=MagickTrue;
5749 #endif
5750                 }
5751
5752             if (length != 0)
5753               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5754
5755             continue;
5756           }
5757
5758         if ((memcmp(type,mng_DISC,4) == 0) || (memcmp(type,mng_SEEK,4) == 0))
5759           {
5760             /* Read DISC or SEEK.  */
5761
5762             if ((length == 0) || !memcmp(type,mng_SEEK,4))
5763               {
5764                 for (i=1; i < MNG_MAX_OBJECTS; i++)
5765                   MngInfoDiscardObject(mng_info,i);
5766               }
5767
5768             else
5769               {
5770                 register ssize_t
5771                   j;
5772
5773                 for (j=1; j < (ssize_t) length; j+=2)
5774                 {
5775                   i=p[j-1] << 8 | p[j];
5776                   MngInfoDiscardObject(mng_info,i);
5777                 }
5778               }
5779
5780             if (length != 0)
5781               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5782
5783             continue;
5784           }
5785
5786         if (memcmp(type,mng_MOVE,4) == 0)
5787           {
5788             size_t
5789               first_object,
5790               last_object;
5791
5792             /* read MOVE */
5793
5794             if (length > 3)
5795             {
5796               first_object=(p[0] << 8) | p[1];
5797               last_object=(p[2] << 8) | p[3];
5798               p+=4;
5799
5800               for (i=(ssize_t) first_object; i <= (ssize_t) last_object; i++)
5801               {
5802                 if (mng_info->exists[i] && !mng_info->frozen[i] &&
5803                     (p-chunk) < (ssize_t) (length-8))
5804                   {
5805                     MngPair
5806                       new_pair;
5807
5808                     MngPair
5809                       old_pair;
5810
5811                     old_pair.a=mng_info->x_off[i];
5812                     old_pair.b=mng_info->y_off[i];
5813                     new_pair=mng_read_pair(old_pair,(int) p[0],&p[1]);
5814                     mng_info->x_off[i]=new_pair.a;
5815                     mng_info->y_off[i]=new_pair.b;
5816                   }
5817               }
5818             }
5819
5820             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5821             continue;
5822           }
5823
5824         if (memcmp(type,mng_LOOP,4) == 0)
5825           {
5826             ssize_t loop_iters=1;
5827             if (length > 4)
5828               {
5829                 loop_level=chunk[0];
5830                 mng_info->loop_active[loop_level]=1;  /* mark loop active */
5831
5832                 /* Record starting point.  */
5833                 loop_iters=mng_get_long(&chunk[1]);
5834
5835                 if (logging != MagickFalse)
5836                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5837                     "  LOOP level %.20g has %.20g iterations ",
5838                     (double) loop_level, (double) loop_iters);
5839
5840                 if (loop_iters == 0)
5841                   skipping_loop=loop_level;
5842
5843                 else
5844                   {
5845                     mng_info->loop_jump[loop_level]=TellBlob(image);
5846                     mng_info->loop_count[loop_level]=loop_iters;
5847                   }
5848
5849                 mng_info->loop_iteration[loop_level]=0;
5850               }
5851             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5852             continue;
5853           }
5854
5855         if (memcmp(type,mng_ENDL,4) == 0)
5856           {
5857             if (length > 0)
5858               {
5859                 loop_level=chunk[0];
5860
5861                 if (skipping_loop > 0)
5862                   {
5863                     if (skipping_loop == loop_level)
5864                       {
5865                         /*
5866                           Found end of zero-iteration loop.
5867                         */
5868                         skipping_loop=(-1);
5869                         mng_info->loop_active[loop_level]=0;
5870                       }
5871                   }
5872
5873                 else
5874                   {
5875                     if (mng_info->loop_active[loop_level] == 1)
5876                       {
5877                         mng_info->loop_count[loop_level]--;
5878                         mng_info->loop_iteration[loop_level]++;
5879
5880                         if (logging != MagickFalse)
5881                           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
5882                           "  ENDL: LOOP level %.20g has %.20g remaining iters ",
5883                             (double) loop_level,(double)
5884                             mng_info->loop_count[loop_level]);
5885
5886                         if (mng_info->loop_count[loop_level] != 0)
5887                           {
5888                             offset=
5889                               SeekBlob(image,mng_info->loop_jump[loop_level],
5890                               SEEK_SET);
5891
5892                             if (offset < 0)
5893                               ThrowReaderException(CorruptImageError,
5894                                 "ImproperImageHeader");
5895                           }
5896
5897                         else
5898                           {
5899                             short
5900                               last_level;
5901
5902                             /*
5903                               Finished loop.
5904                             */
5905                             mng_info->loop_active[loop_level]=0;
5906                             last_level=(-1);
5907                             for (i=0; i < loop_level; i++)
5908                               if (mng_info->loop_active[i] == 1)
5909                                 last_level=(short) i;
5910                             loop_level=last_level;
5911                           }
5912                       }
5913                   }
5914               }
5915
5916             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
5917             continue;
5918           }
5919
5920         if (memcmp(type,mng_CLON,4) == 0)
5921           {
5922             if (mng_info->clon_warning == 0)
5923               (void) ThrowMagickException(exception,GetMagickModule(),
5924                 CoderError,"CLON is not implemented yet","`%s'",
5925                 image->filename);
5926
5927             mng_info->clon_warning++;
5928           }
5929
5930         if (memcmp(type,mng_MAGN,4) == 0)
5931           {
5932             png_uint_16
5933               magn_first,
5934               magn_last,
5935               magn_mb,
5936               magn_ml,
5937               magn_mr,
5938               magn_mt,
5939               magn_mx,
5940               magn_my,
5941               magn_methx,
5942               magn_methy;
5943
5944             if (length > 1)
5945               magn_first=(p[0] << 8) | p[1];
5946
5947             else
5948               magn_first=0;
5949
5950             if (length > 3)
5951               magn_last=(p[2] << 8) | p[3];
5952
5953             else
5954               magn_last=magn_first;
5955 #ifndef MNG_OBJECT_BUFFERS
5956             if (magn_first || magn_last)
5957               if (mng_info->magn_warning == 0)
5958                 {
5959                   (void) ThrowMagickException(exception,
5960                      GetMagickModule(),CoderError,
5961                      "MAGN is not implemented yet for nonzero objects",
5962                      "`%s'",image->filename);
5963
5964                    mng_info->magn_warning++;
5965                 }
5966 #endif
5967             if (length > 4)
5968               magn_methx=p[4];
5969
5970             else
5971               magn_methx=0;
5972
5973             if (length > 6)
5974               magn_mx=(p[5] << 8) | p[6];
5975
5976             else
5977               magn_mx=1;
5978
5979             if (magn_mx == 0)
5980               magn_mx=1;
5981
5982             if (length > 8)
5983               magn_my=(p[7] << 8) | p[8];
5984
5985             else
5986               magn_my=magn_mx;
5987
5988             if (magn_my == 0)
5989               magn_my=1;
5990
5991             if (length > 10)
5992               magn_ml=(p[9] << 8) | p[10];
5993
5994             else
5995               magn_ml=magn_mx;
5996
5997             if (magn_ml == 0)
5998               magn_ml=1;
5999
6000             if (length > 12)
6001               magn_mr=(p[11] << 8) | p[12];
6002
6003             else
6004               magn_mr=magn_mx;
6005
6006             if (magn_mr == 0)
6007               magn_mr=1;
6008
6009             if (length > 14)
6010               magn_mt=(p[13] << 8) | p[14];
6011
6012             else
6013               magn_mt=magn_my;
6014
6015             if (magn_mt == 0)
6016               magn_mt=1;
6017
6018             if (length > 16)
6019               magn_mb=(p[15] << 8) | p[16];
6020
6021             else
6022               magn_mb=magn_my;
6023
6024             if (magn_mb == 0)
6025               magn_mb=1;
6026
6027             if (length > 17)
6028               magn_methy=p[17];
6029
6030             else
6031               magn_methy=magn_methx;
6032
6033
6034             if (magn_methx > 5 || magn_methy > 5)
6035               if (mng_info->magn_warning == 0)
6036                 {
6037                   (void) ThrowMagickException(exception,
6038                      GetMagickModule(),CoderError,
6039                      "Unknown MAGN method in MNG datastream","`%s'",
6040                      image->filename);
6041
6042                    mng_info->magn_warning++;
6043                 }
6044 #ifdef MNG_OBJECT_BUFFERS
6045           /* Magnify existing objects in the range magn_first to magn_last */
6046 #endif
6047             if (magn_first == 0 || magn_last == 0)
6048               {
6049                 /* Save the magnification factors for object 0 */
6050                 mng_info->magn_mb=magn_mb;
6051                 mng_info->magn_ml=magn_ml;
6052                 mng_info->magn_mr=magn_mr;
6053                 mng_info->magn_mt=magn_mt;
6054                 mng_info->magn_mx=magn_mx;
6055                 mng_info->magn_my=magn_my;
6056                 mng_info->magn_methx=magn_methx;
6057                 mng_info->magn_methy=magn_methy;
6058               }
6059           }
6060
6061         if (memcmp(type,mng_PAST,4) == 0)
6062           {
6063             if (mng_info->past_warning == 0)
6064               (void) ThrowMagickException(exception,GetMagickModule(),
6065                 CoderError,"PAST is not implemented yet","`%s'",
6066                 image->filename);
6067
6068             mng_info->past_warning++;
6069           }
6070
6071         if (memcmp(type,mng_SHOW,4) == 0)
6072           {
6073             if (mng_info->show_warning == 0)
6074               (void) ThrowMagickException(exception,GetMagickModule(),
6075                 CoderError,"SHOW is not implemented yet","`%s'",
6076                 image->filename);
6077
6078             mng_info->show_warning++;
6079           }
6080
6081         if (memcmp(type,mng_sBIT,4) == 0)
6082           {
6083             if (length < 4)
6084               mng_info->have_global_sbit=MagickFalse;
6085
6086             else
6087               {
6088                 mng_info->global_sbit.gray=p[0];
6089                 mng_info->global_sbit.red=p[0];
6090                 mng_info->global_sbit.green=p[1];
6091                 mng_info->global_sbit.blue=p[2];
6092                 mng_info->global_sbit.alpha=p[3];
6093                 mng_info->have_global_sbit=MagickTrue;
6094              }
6095           }
6096         if (memcmp(type,mng_pHYs,4) == 0)
6097           {
6098             if (length > 8)
6099               {
6100                 mng_info->global_x_pixels_per_unit=
6101                     (size_t) mng_get_long(p);
6102                 mng_info->global_y_pixels_per_unit=
6103                     (size_t) mng_get_long(&p[4]);
6104                 mng_info->global_phys_unit_type=p[8];
6105                 mng_info->have_global_phys=MagickTrue;
6106               }
6107
6108             else
6109               mng_info->have_global_phys=MagickFalse;
6110           }
6111         if (memcmp(type,mng_pHYg,4) == 0)
6112           {
6113             if (mng_info->phyg_warning == 0)
6114               (void) ThrowMagickException(exception,GetMagickModule(),
6115                 CoderError,"pHYg is not implemented.","`%s'",image->filename);
6116
6117             mng_info->phyg_warning++;
6118           }
6119         if (memcmp(type,mng_BASI,4) == 0)
6120           {
6121             skip_to_iend=MagickTrue;
6122
6123             if (mng_info->basi_warning == 0)
6124               (void) ThrowMagickException(exception,GetMagickModule(),
6125                 CoderError,"BASI is not implemented yet","`%s'",
6126                 image->filename);
6127
6128             mng_info->basi_warning++;
6129 #ifdef MNG_BASI_SUPPORTED
6130             basi_width=(size_t) ((p[0] << 24) | (p[1] << 16) |
6131                (p[2] << 8) | p[3]);
6132             basi_height=(size_t) ((p[4] << 24) | (p[5] << 16) |
6133                (p[6] << 8) | p[7]);
6134             basi_color_type=p[8];
6135             basi_compression_method=p[9];
6136             basi_filter_type=p[10];
6137             basi_interlace_method=p[11];
6138             if (length > 11)
6139               basi_red=(p[12] << 8) & p[13];
6140
6141             else
6142               basi_red=0;
6143
6144             if (length > 13)
6145               basi_green=(p[14] << 8) & p[15];
6146
6147             else
6148               basi_green=0;
6149
6150             if (length > 15)
6151               basi_blue=(p[16] << 8) & p[17];
6152
6153             else
6154               basi_blue=0;
6155
6156             if (length > 17)
6157               basi_alpha=(p[18] << 8) & p[19];
6158
6159             else
6160               {
6161                 if (basi_sample_depth == 16)
6162                   basi_alpha=65535L;
6163                 else
6164                   basi_alpha=255;
6165               }
6166
6167             if (length > 19)
6168               basi_viewable=p[20];
6169
6170             else
6171               basi_viewable=0;
6172
6173 #endif
6174             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6175             continue;
6176           }
6177
6178         if (memcmp(type,mng_IHDR,4)
6179 #if defined(JNG_SUPPORTED)
6180             && memcmp(type,mng_JHDR,4)
6181 #endif
6182             )
6183           {
6184             /* Not an IHDR or JHDR chunk */
6185             if (length != 0)
6186               chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6187
6188             continue;
6189           }
6190 /* Process IHDR */
6191         if (logging != MagickFalse)
6192           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6193             "  Processing %c%c%c%c chunk",type[0],type[1],type[2],type[3]);
6194
6195         mng_info->exists[object_id]=MagickTrue;
6196         mng_info->viewable[object_id]=MagickTrue;
6197
6198         if (mng_info->invisible[object_id])
6199           {
6200             if (logging != MagickFalse)
6201               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6202                 "  Skipping invisible object");
6203
6204             skip_to_iend=MagickTrue;
6205             chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6206             continue;
6207           }
6208 #if defined(MNG_INSERT_LAYERS)
6209         if (length < 8)
6210           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6211
6212         image_width=(size_t) mng_get_long(p);
6213         image_height=(size_t) mng_get_long(&p[4]);
6214 #endif
6215         chunk=(unsigned char *) RelinquishMagickMemory(chunk);
6216
6217         /*
6218           Insert a transparent background layer behind the entire animation
6219           if it is not full screen.
6220         */
6221 #if defined(MNG_INSERT_LAYERS)
6222         if (insert_layers && mng_type && first_mng_object)
6223           {
6224             if ((mng_info->clip.left > 0) || (mng_info->clip.top > 0) ||
6225                 (image_width < mng_info->mng_width) ||
6226                 (mng_info->clip.right < (ssize_t) mng_info->mng_width) ||
6227                 (image_height < mng_info->mng_height) ||
6228                 (mng_info->clip.bottom < (ssize_t) mng_info->mng_height))
6229               {
6230                 if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6231                   {
6232                     /*
6233                       Allocate next image structure.
6234                     */
6235                     AcquireNextImage(image_info,image,exception);
6236
6237                     if (GetNextImageInList(image) == (Image *) NULL)
6238                       {
6239                         image=DestroyImageList(image);
6240                         MngInfoFreeStruct(mng_info,&have_mng_structure);
6241                         return((Image *) NULL);
6242                       }
6243
6244                     image=SyncNextImageInList(image);
6245                   }
6246                 mng_info->image=image;
6247
6248                 if (term_chunk_found)
6249                   {
6250                     image->start_loop=MagickTrue;
6251                     image->iterations=mng_iterations;
6252                     term_chunk_found=MagickFalse;
6253                   }
6254
6255                 else
6256                     image->start_loop=MagickFalse;
6257
6258                 /* Make a background rectangle.  */
6259
6260                 image->delay=0;
6261                 image->columns=mng_info->mng_width;
6262                 image->rows=mng_info->mng_height;
6263                 image->page.width=mng_info->mng_width;
6264                 image->page.height=mng_info->mng_height;
6265                 image->page.x=0;
6266                 image->page.y=0;
6267                 image->background_color=mng_background_color;
6268                 (void) SetImageBackgroundColor(image,exception);
6269                 if (logging != MagickFalse)
6270                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6271                     "  Inserted transparent background layer, W=%.20g, H=%.20g",
6272                     (double) mng_info->mng_width,(double) mng_info->mng_height);
6273               }
6274           }
6275         /*
6276           Insert a background layer behind the upcoming image if
6277           framing_mode is 3, and we haven't already inserted one.
6278         */
6279         if (insert_layers && (mng_info->framing_mode == 3) &&
6280                 (subframe_width) && (subframe_height) && (simplicity == 0 ||
6281                 (simplicity & 0x08)))
6282           {
6283             if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6284             {
6285               /*
6286                 Allocate next image structure.
6287               */
6288               AcquireNextImage(image_info,image,exception);
6289
6290               if (GetNextImageInList(image) == (Image *) NULL)
6291                 {
6292                   image=DestroyImageList(image);
6293                   MngInfoFreeStruct(mng_info,&have_mng_structure);
6294                   return((Image *) NULL);
6295                 }
6296
6297               image=SyncNextImageInList(image);
6298             }
6299
6300             mng_info->image=image;
6301
6302             if (term_chunk_found)
6303               {
6304                 image->start_loop=MagickTrue;
6305                 image->iterations=mng_iterations;
6306                 term_chunk_found=MagickFalse;
6307               }
6308
6309             else
6310                 image->start_loop=MagickFalse;
6311
6312             image->delay=0;
6313             image->columns=subframe_width;
6314             image->rows=subframe_height;
6315             image->page.width=subframe_width;
6316             image->page.height=subframe_height;
6317             image->page.x=mng_info->clip.left;
6318             image->page.y=mng_info->clip.top;
6319             image->background_color=mng_background_color;
6320             image->alpha_trait=UndefinedPixelTrait;
6321             (void) SetImageBackgroundColor(image,exception);
6322
6323             if (logging != MagickFalse)
6324               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6325                 "  Insert background layer, L=%.20g, R=%.20g T=%.20g, B=%.20g",
6326                 (double) mng_info->clip.left,(double) mng_info->clip.right,
6327                 (double) mng_info->clip.top,(double) mng_info->clip.bottom);
6328           }
6329 #endif /* MNG_INSERT_LAYERS */
6330         first_mng_object=MagickFalse;
6331
6332         if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
6333           {
6334             /*
6335               Allocate next image structure.
6336             */
6337             AcquireNextImage(image_info,image,exception);
6338
6339             if (GetNextImageInList(image) == (Image *) NULL)
6340               {
6341                 image=DestroyImageList(image);
6342                 MngInfoFreeStruct(mng_info,&have_mng_structure);
6343                 return((Image *) NULL);
6344               }
6345
6346             image=SyncNextImageInList(image);
6347           }
6348         mng_info->image=image;
6349         status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
6350           GetBlobSize(image));
6351
6352         if (status == MagickFalse)
6353           break;
6354
6355         if (term_chunk_found)
6356           {
6357             image->start_loop=MagickTrue;
6358             term_chunk_found=MagickFalse;
6359           }
6360
6361         else
6362             image->start_loop=MagickFalse;
6363
6364         if (mng_info->framing_mode == 1 || mng_info->framing_mode == 3)
6365           {
6366             image->delay=frame_delay;
6367             frame_delay=default_frame_delay;
6368           }
6369
6370         else
6371           image->delay=0;
6372
6373         image->page.width=mng_info->mng_width;
6374         image->page.height=mng_info->mng_height;
6375         image->page.x=mng_info->x_off[object_id];
6376         image->page.y=mng_info->y_off[object_id];
6377         image->iterations=mng_iterations;
6378
6379         /*
6380           Seek back to the beginning of the IHDR or JHDR chunk's length field.
6381         */
6382
6383         if (logging != MagickFalse)
6384           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6385             "  Seeking back to beginning of %c%c%c%c chunk",type[0],type[1],
6386             type[2],type[3]);
6387
6388         offset=SeekBlob(image,-((ssize_t) length+12),SEEK_CUR);
6389
6390         if (offset < 0)
6391           ThrowReaderException(CorruptImageError,"ImproperImageHeader");
6392       }
6393
6394     mng_info->image=image;
6395     mng_info->mng_type=mng_type;
6396     mng_info->object_id=object_id;
6397
6398     if (memcmp(type,mng_IHDR,4) == 0)
6399       image=ReadOnePNGImage(mng_info,image_info,exception);
6400
6401 #if defined(JNG_SUPPORTED)
6402     else
6403       image=ReadOneJNGImage(mng_info,image_info,exception);
6404 #endif
6405
6406     if (image == (Image *) NULL)
6407       {
6408         if (logging != MagickFalse)
6409           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6410             "exit ReadJNGImage() with error");
6411
6412         MngInfoFreeStruct(mng_info,&have_mng_structure);
6413         return((Image *) NULL);
6414       }
6415
6416     if (image->columns == 0 || image->rows == 0)
6417       {
6418         (void) CloseBlob(image);
6419         image=DestroyImageList(image);
6420         MngInfoFreeStruct(mng_info,&have_mng_structure);
6421         return((Image *) NULL);
6422       }
6423
6424     mng_info->image=image;
6425
6426     if (mng_type)
6427       {
6428         MngBox
6429           crop_box;
6430
6431         if (mng_info->magn_methx || mng_info->magn_methy)
6432           {
6433             png_uint_32
6434                magnified_height,
6435                magnified_width;
6436
6437             if (logging != MagickFalse)
6438               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6439                 "  Processing MNG MAGN chunk");
6440
6441             if (mng_info->magn_methx == 1)
6442               {
6443                 magnified_width=mng_info->magn_ml;
6444
6445                 if (image->columns > 1)
6446                    magnified_width += mng_info->magn_mr;
6447
6448                 if (image->columns > 2)
6449                    magnified_width += (png_uint_32)
6450                       ((image->columns-2)*(mng_info->magn_mx));
6451               }
6452
6453             else
6454               {
6455                 magnified_width=(png_uint_32) image->columns;
6456
6457                 if (image->columns > 1)
6458                    magnified_width += mng_info->magn_ml-1;
6459
6460                 if (image->columns > 2)
6461                    magnified_width += mng_info->magn_mr-1;
6462
6463                 if (image->columns > 3)
6464                    magnified_width += (png_uint_32)
6465                       ((image->columns-3)*(mng_info->magn_mx-1));
6466               }
6467
6468             if (mng_info->magn_methy == 1)
6469               {
6470                 magnified_height=mng_info->magn_mt;
6471
6472                 if (image->rows > 1)
6473                    magnified_height += mng_info->magn_mb;
6474
6475                 if (image->rows > 2)
6476                    magnified_height += (png_uint_32)
6477                       ((image->rows-2)*(mng_info->magn_my));
6478               }
6479
6480             else
6481               {
6482                 magnified_height=(png_uint_32) image->rows;
6483
6484                 if (image->rows > 1)
6485                    magnified_height += mng_info->magn_mt-1;
6486
6487                 if (image->rows > 2)
6488                    magnified_height += mng_info->magn_mb-1;
6489
6490                 if (image->rows > 3)
6491                    magnified_height += (png_uint_32)
6492                       ((image->rows-3)*(mng_info->magn_my-1));
6493               }
6494
6495             if (magnified_height > image->rows ||
6496                 magnified_width > image->columns)
6497               {
6498                 Image
6499                   *large_image;
6500
6501                 int
6502                   yy;
6503
6504                 Quantum
6505                   *next,
6506                   *prev;
6507
6508                 png_uint_16
6509                   magn_methx,
6510                   magn_methy;
6511
6512                 ssize_t
6513                   m,
6514                   y;
6515
6516                 register Quantum
6517                   *n,
6518                   *q;
6519
6520                 register ssize_t
6521                   x;
6522
6523                 /* Allocate next image structure.  */
6524
6525                 if (logging != MagickFalse)
6526                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6527                     "    Allocate magnified image");
6528
6529                 AcquireNextImage(image_info,image,exception);
6530
6531                 if (GetNextImageInList(image) == (Image *) NULL)
6532                   {
6533                     image=DestroyImageList(image);
6534                     MngInfoFreeStruct(mng_info,&have_mng_structure);
6535                     return((Image *) NULL);
6536                   }
6537
6538                 large_image=SyncNextImageInList(image);
6539
6540                 large_image->columns=magnified_width;
6541                 large_image->rows=magnified_height;
6542
6543                 magn_methx=mng_info->magn_methx;
6544                 magn_methy=mng_info->magn_methy;
6545
6546 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6547 #define QM unsigned short
6548                 if (magn_methx != 1 || magn_methy != 1)
6549                   {
6550                   /*
6551                      Scale pixels to unsigned shorts to prevent
6552                      overflow of intermediate values of interpolations
6553                   */
6554                      for (y=0; y < (ssize_t) image->rows; y++)
6555                      {
6556                        q=GetAuthenticPixels(image,0,y,image->columns,1,
6557                           exception);
6558
6559                        for (x=(ssize_t) image->columns-1; x >= 0; x--)
6560                        {
6561                           SetPixelRed(image,ScaleQuantumToShort(
6562                             GetPixelRed(image,q)),q);
6563                           SetPixelGreen(image,ScaleQuantumToShort(
6564                             GetPixelGreen(image,q)),q);
6565                           SetPixelBlue(image,ScaleQuantumToShort(
6566                             GetPixelBlue(image,q)),q);
6567                           SetPixelAlpha(image,ScaleQuantumToShort(
6568                             GetPixelAlpha(image,q)),q);
6569                           q+=GetPixelChannels(image);
6570                        }
6571
6572                        if (SyncAuthenticPixels(image,exception) == MagickFalse)
6573                          break;
6574                      }
6575                   }
6576 #else
6577 #define QM Quantum
6578 #endif
6579
6580                 if (image->alpha_trait != UndefinedPixelTrait)
6581                    (void) SetImageBackgroundColor(large_image,exception);
6582
6583                 else
6584                   {
6585                     large_image->background_color.alpha=OpaqueAlpha;
6586                     (void) SetImageBackgroundColor(large_image,exception);
6587
6588                     if (magn_methx == 4)
6589                       magn_methx=2;
6590
6591                     if (magn_methx == 5)
6592                       magn_methx=3;
6593
6594                     if (magn_methy == 4)
6595                       magn_methy=2;
6596
6597                     if (magn_methy == 5)
6598                       magn_methy=3;
6599                   }
6600
6601                 /* magnify the rows into the right side of the large image */
6602
6603                 if (logging != MagickFalse)
6604                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6605                     "    Magnify the rows to %.20g",(double) large_image->rows);
6606                 m=(ssize_t) mng_info->magn_mt;
6607                 yy=0;
6608                 length=(size_t) GetPixelChannels(image)*image->columns;
6609                 next=(Quantum *) AcquireQuantumMemory(length,sizeof(*next));
6610                 prev=(Quantum *) AcquireQuantumMemory(length,sizeof(*prev));
6611
6612                 if ((prev == (Quantum *) NULL) ||
6613                     (next == (Quantum *) NULL))
6614                   {
6615                      image=DestroyImageList(image);
6616                      MngInfoFreeStruct(mng_info,&have_mng_structure);
6617                      ThrowReaderException(ResourceLimitError,
6618                        "MemoryAllocationFailed");
6619                   }
6620
6621                 n=GetAuthenticPixels(image,0,0,image->columns,1,exception);
6622                 (void) CopyMagickMemory(next,n,length);
6623
6624                 for (y=0; y < (ssize_t) image->rows; y++)
6625                 {
6626                   if (y == 0)
6627                     m=(ssize_t) mng_info->magn_mt;
6628
6629                   else if (magn_methy > 1 && y == (ssize_t) image->rows-2)
6630                     m=(ssize_t) mng_info->magn_mb;
6631
6632                   else if (magn_methy <= 1 && y == (ssize_t) image->rows-1)
6633                     m=(ssize_t) mng_info->magn_mb;
6634
6635                   else if (magn_methy > 1 && y == (ssize_t) image->rows-1)
6636                     m=1;
6637
6638                   else
6639                     m=(ssize_t) mng_info->magn_my;
6640
6641                   n=prev;
6642                   prev=next;
6643                   next=n;
6644
6645                   if (y < (ssize_t) image->rows-1)
6646                     {
6647                       n=GetAuthenticPixels(image,0,y+1,image->columns,1,
6648                           exception);
6649                       (void) CopyMagickMemory(next,n,length);
6650                     }
6651
6652                   for (i=0; i < m; i++, yy++)
6653                   {
6654                     register Quantum
6655                       *pixels;
6656
6657                     assert(yy < (ssize_t) large_image->rows);
6658                     pixels=prev;
6659                     n=next;
6660                     q=GetAuthenticPixels(large_image,0,yy,large_image->columns,
6661                       1,exception);
6662                     q+=(large_image->columns-image->columns)*
6663                       GetPixelChannels(large_image);
6664
6665                     for (x=(ssize_t) image->columns-1; x >= 0; x--)
6666                     {
6667                       /* To do: get color as function of indexes[x] */
6668                       /*
6669                       if (image->storage_class == PseudoClass)
6670                         {
6671                         }
6672                       */
6673
6674                       if (magn_methy <= 1)
6675                         {
6676                           /* replicate previous */
6677                           SetPixelRed(large_image,GetPixelRed(image,pixels),q);
6678                           SetPixelGreen(large_image,GetPixelGreen(image,
6679                              pixels),q);
6680                           SetPixelBlue(large_image,GetPixelBlue(image,
6681                              pixels),q);
6682                           SetPixelAlpha(large_image,GetPixelAlpha(image,
6683                              pixels),q);
6684                         }
6685
6686                       else if (magn_methy == 2 || magn_methy == 4)
6687                         {
6688                           if (i == 0)
6689                             {
6690                               SetPixelRed(large_image,GetPixelRed(image,
6691                                  pixels),q);
6692                               SetPixelGreen(large_image,GetPixelGreen(image,
6693                                  pixels),q);
6694                               SetPixelBlue(large_image,GetPixelBlue(image,
6695                                  pixels),q);
6696                               SetPixelAlpha(large_image,GetPixelAlpha(image,
6697                                  pixels),q);
6698                             }
6699
6700                           else
6701                             {
6702                               /* Interpolate */
6703                               SetPixelRed(large_image,((QM) (((ssize_t)
6704                                  (2*i*(GetPixelRed(image,n)
6705                                  -GetPixelRed(image,pixels)+m))/
6706                                  ((ssize_t) (m*2))
6707                                  +GetPixelRed(image,pixels)))),q);
6708                               SetPixelGreen(large_image,((QM) (((ssize_t)
6709                                  (2*i*(GetPixelGreen(image,n)
6710                                  -GetPixelGreen(image,pixels)+m))/
6711                                  ((ssize_t) (m*2))
6712                                  +GetPixelGreen(image,pixels)))),q);
6713                               SetPixelBlue(large_image,((QM) (((ssize_t)
6714                                  (2*i*(GetPixelBlue(image,n)
6715                                  -GetPixelBlue(image,pixels)+m))/
6716                                  ((ssize_t) (m*2))
6717                                  +GetPixelBlue(image,pixels)))),q);
6718
6719                               if (image->alpha_trait != UndefinedPixelTrait)
6720                                  SetPixelAlpha(large_image, ((QM) (((ssize_t)
6721                                     (2*i*(GetPixelAlpha(image,n)
6722                                     -GetPixelAlpha(image,pixels)+m))
6723                                     /((ssize_t) (m*2))+
6724                                    GetPixelAlpha(image,pixels)))),q);
6725                             }
6726
6727                           if (magn_methy == 4)
6728                             {
6729                               /* Replicate nearest */
6730                               if (i <= ((m+1) << 1))
6731                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6732                                     pixels),q);
6733                               else
6734                                  SetPixelAlpha(large_image,GetPixelAlpha(image,
6735                                     n),q);
6736                             }
6737                         }
6738
6739                       else /* if (magn_methy == 3 || magn_methy == 5) */
6740                         {
6741                           /* Replicate nearest */
6742                           if (i <= ((m+1) << 1))
6743                           {
6744                              SetPixelRed(large_image,GetPixelRed(image,
6745                                     pixels),q);
6746                              SetPixelGreen(large_image,GetPixelGreen(image,
6747                                     pixels),q);
6748                              SetPixelBlue(large_image,GetPixelBlue(image,
6749                                     pixels),q);
6750                              SetPixelAlpha(large_image,GetPixelAlpha(image,
6751                                     pixels),q);
6752                           }
6753
6754                           else
6755                           {
6756                              SetPixelRed(large_image,GetPixelRed(image,n),q);
6757                              SetPixelGreen(large_image,GetPixelGreen(image,n),
6758                                     q);
6759                              SetPixelBlue(large_image,GetPixelBlue(image,n),
6760                                     q);
6761                              SetPixelAlpha(large_image,GetPixelAlpha(image,n),
6762                                     q);
6763                           }
6764
6765                           if (magn_methy == 5)
6766                             {
6767                               SetPixelAlpha(large_image,(QM) (((ssize_t) (2*i*
6768                                  (GetPixelAlpha(image,n)
6769                                  -GetPixelAlpha(image,pixels))
6770                                  +m))/((ssize_t) (m*2))
6771                                  +GetPixelAlpha(image,pixels)),q);
6772                             }
6773                         }
6774                       n+=GetPixelChannels(image);
6775                       q+=GetPixelChannels(large_image);
6776                       pixels+=GetPixelChannels(image);
6777                     } /* x */
6778
6779                     if (SyncAuthenticPixels(large_image,exception) == 0)
6780                       break;
6781
6782                   } /* i */
6783                 } /* y */
6784
6785                 prev=(Quantum *) RelinquishMagickMemory(prev);
6786                 next=(Quantum *) RelinquishMagickMemory(next);
6787
6788                 length=image->columns;
6789
6790                 if (logging != MagickFalse)
6791                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6792                     "    Delete original image");
6793
6794                 DeleteImageFromList(&image);
6795
6796                 image=large_image;
6797
6798                 mng_info->image=image;
6799
6800                 /* magnify the columns */
6801                 if (logging != MagickFalse)
6802                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6803                     "    Magnify the columns to %.20g",(double) image->columns);
6804
6805                 for (y=0; y < (ssize_t) image->rows; y++)
6806                 {
6807                   register Quantum
6808                     *pixels;
6809
6810                   q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6811                   pixels=q+(image->columns-length)*GetPixelChannels(image);
6812                   n=pixels+GetPixelChannels(image);
6813
6814                   for (x=(ssize_t) (image->columns-length);
6815                     x < (ssize_t) image->columns; x++)
6816                   {
6817                     /* To do: Rewrite using Get/Set***PixelChannel() */
6818
6819                     if (x == (ssize_t) (image->columns-length))
6820                       m=(ssize_t) mng_info->magn_ml;
6821
6822                     else if (magn_methx > 1 && x == (ssize_t) image->columns-2)
6823                       m=(ssize_t) mng_info->magn_mr;
6824
6825                     else if (magn_methx <= 1 && x == (ssize_t) image->columns-1)
6826                       m=(ssize_t) mng_info->magn_mr;
6827
6828                     else if (magn_methx > 1 && x == (ssize_t) image->columns-1)
6829                       m=1;
6830
6831                     else
6832                       m=(ssize_t) mng_info->magn_mx;
6833
6834                     for (i=0; i < m; i++)
6835                     {
6836                       if (magn_methx <= 1)
6837                         {
6838                           /* replicate previous */
6839                           SetPixelRed(image,GetPixelRed(image,pixels),q);
6840                           SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6841                           SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6842                           SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6843                         }
6844
6845                       else if (magn_methx == 2 || magn_methx == 4)
6846                         {
6847                           if (i == 0)
6848                           {
6849                             SetPixelRed(image,GetPixelRed(image,pixels),q);
6850                             SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6851                             SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6852                             SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6853                           }
6854
6855                           /* To do: Rewrite using Get/Set***PixelChannel() */
6856                           else
6857                             {
6858                               /* Interpolate */
6859                               SetPixelRed(image,(QM) ((2*i*(
6860                                  GetPixelRed(image,n)
6861                                  -GetPixelRed(image,pixels))+m)
6862                                  /((ssize_t) (m*2))+
6863                                  GetPixelRed(image,pixels)),q);
6864
6865                               SetPixelGreen(image,(QM) ((2*i*(
6866                                  GetPixelGreen(image,n)
6867                                  -GetPixelGreen(image,pixels))+m)
6868                                  /((ssize_t) (m*2))+
6869                                  GetPixelGreen(image,pixels)),q);
6870
6871                               SetPixelBlue(image,(QM) ((2*i*(
6872                                  GetPixelBlue(image,n)
6873                                  -GetPixelBlue(image,pixels))+m)
6874                                  /((ssize_t) (m*2))+
6875                                  GetPixelBlue(image,pixels)),q);
6876                               if (image->alpha_trait != UndefinedPixelTrait)
6877                                  SetPixelAlpha(image,(QM) ((2*i*(
6878                                    GetPixelAlpha(image,n)
6879                                    -GetPixelAlpha(image,pixels))+m)
6880                                    /((ssize_t) (m*2))+
6881                                    GetPixelAlpha(image,pixels)),q);
6882                             }
6883
6884                           if (magn_methx == 4)
6885                             {
6886                               /* Replicate nearest */
6887                               if (i <= ((m+1) << 1))
6888                               {
6889                                  SetPixelAlpha(image,
6890                                    GetPixelAlpha(image,pixels)+0,q);
6891                               }
6892                               else
6893                               {
6894                                  SetPixelAlpha(image,
6895                                    GetPixelAlpha(image,n)+0,q);
6896                               }
6897                             }
6898                         }
6899
6900                       else /* if (magn_methx == 3 || magn_methx == 5) */
6901                         {
6902                           /* Replicate nearest */
6903                           if (i <= ((m+1) << 1))
6904                           {
6905                              SetPixelRed(image,GetPixelRed(image,pixels),q);
6906                              SetPixelGreen(image,GetPixelGreen(image,pixels),q);
6907                              SetPixelBlue(image,GetPixelBlue(image,pixels),q);
6908                              SetPixelAlpha(image,GetPixelAlpha(image,pixels),q);
6909                           }
6910
6911                           else
6912                           {
6913                              SetPixelRed(image,GetPixelRed(image,n),q);
6914                              SetPixelGreen(image,GetPixelGreen(image,n),q);
6915                              SetPixelBlue(image,GetPixelBlue(image,n),q);
6916                              SetPixelAlpha(image,GetPixelAlpha(image,n),q);
6917                           }
6918
6919                           if (magn_methx == 5)
6920                             {
6921                               /* Interpolate */
6922                               SetPixelAlpha(image,
6923                                  (QM) ((2*i*( GetPixelAlpha(image,n)
6924                                  -GetPixelAlpha(image,pixels))+m)/
6925                                  ((ssize_t) (m*2))
6926                                  +GetPixelAlpha(image,pixels)),q);
6927                             }
6928                         }
6929                       q+=GetPixelChannels(image);
6930                     }
6931                     n+=GetPixelChannels(image);
6932                   }
6933
6934                   if (SyncAuthenticPixels(image,exception) == MagickFalse)
6935                     break;
6936                 }
6937 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
6938               if (magn_methx != 1 || magn_methy != 1)
6939                 {
6940                 /*
6941                    Rescale pixels to Quantum
6942                 */
6943                    for (y=0; y < (ssize_t) image->rows; y++)
6944                    {
6945                      q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
6946
6947                      for (x=(ssize_t) image->columns-1; x >= 0; x--)
6948                      {
6949                         SetPixelRed(image,ScaleShortToQuantum(
6950                           GetPixelRed(image,q)),q);
6951                         SetPixelGreen(image,ScaleShortToQuantum(
6952                           GetPixelGreen(image,q)),q);
6953                         SetPixelBlue(image,ScaleShortToQuantum(
6954                           GetPixelBlue(image,q)),q);
6955                         SetPixelAlpha(image,ScaleShortToQuantum(
6956                           GetPixelAlpha(image,q)),q);
6957                         q+=GetPixelChannels(image);
6958                      }
6959
6960                      if (SyncAuthenticPixels(image,exception) == MagickFalse)
6961                        break;
6962                    }
6963                 }
6964 #endif
6965                 if (logging != MagickFalse)
6966                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6967                     "  Finished MAGN processing");
6968               }
6969           }
6970
6971         /*
6972           Crop_box is with respect to the upper left corner of the MNG.
6973         */
6974         crop_box.left=mng_info->image_box.left+mng_info->x_off[object_id];
6975         crop_box.right=mng_info->image_box.right+mng_info->x_off[object_id];
6976         crop_box.top=mng_info->image_box.top+mng_info->y_off[object_id];
6977         crop_box.bottom=mng_info->image_box.bottom+mng_info->y_off[object_id];
6978         crop_box=mng_minimum_box(crop_box,mng_info->clip);
6979         crop_box=mng_minimum_box(crop_box,mng_info->frame);
6980         crop_box=mng_minimum_box(crop_box,mng_info->object_clip[object_id]);
6981         if ((crop_box.left != (mng_info->image_box.left
6982             +mng_info->x_off[object_id])) ||
6983             (crop_box.right != (mng_info->image_box.right
6984             +mng_info->x_off[object_id])) ||
6985             (crop_box.top != (mng_info->image_box.top
6986             +mng_info->y_off[object_id])) ||
6987             (crop_box.bottom != (mng_info->image_box.bottom
6988             +mng_info->y_off[object_id])))
6989           {
6990             if (logging != MagickFalse)
6991               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
6992                 "  Crop the PNG image");
6993
6994             if ((crop_box.left < crop_box.right) &&
6995                 (crop_box.top < crop_box.bottom))
6996               {
6997                 Image
6998                   *im;
6999
7000                 RectangleInfo
7001                   crop_info;
7002
7003                 /*
7004                   Crop_info is with respect to the upper left corner of
7005                   the image.
7006                 */
7007                 crop_info.x=(crop_box.left-mng_info->x_off[object_id]);
7008                 crop_info.y=(crop_box.top-mng_info->y_off[object_id]);
7009                 crop_info.width=(size_t) (crop_box.right-crop_box.left);
7010                 crop_info.height=(size_t) (crop_box.bottom-crop_box.top);
7011                 image->page.width=image->columns;
7012                 image->page.height=image->rows;
7013                 image->page.x=0;
7014                 image->page.y=0;
7015                 im=CropImage(image,&crop_info,exception);
7016
7017                 if (im != (Image *) NULL)
7018                   {
7019                     image->columns=im->columns;
7020                     image->rows=im->rows;
7021                     im=DestroyImage(im);
7022                     image->page.width=image->columns;
7023                     image->page.height=image->rows;
7024                     image->page.x=crop_box.left;
7025                     image->page.y=crop_box.top;
7026                   }
7027               }
7028
7029             else
7030               {
7031                 /*
7032                   No pixels in crop area.  The MNG spec still requires
7033                   a layer, though, so make a single transparent pixel in
7034                   the top left corner.
7035                 */
7036                 image->columns=1;
7037                 image->rows=1;
7038                 image->colors=2;
7039                 (void) SetImageBackgroundColor(image,exception);
7040                 image->page.width=1;
7041                 image->page.height=1;
7042                 image->page.x=0;
7043                 image->page.y=0;
7044               }
7045           }
7046 #ifndef PNG_READ_EMPTY_PLTE_SUPPORTED
7047         image=mng_info->image;
7048 #endif
7049       }
7050
7051 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
7052       /* PNG does not handle depths greater than 16 so reduce it even
7053        * if lossy.
7054        */
7055       if (image->depth > 16)
7056          image->depth=16;
7057 #endif
7058
7059 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
7060       if (image->depth > 8)
7061         {
7062           /* To do: fill low byte properly */
7063           image->depth=16;
7064         }
7065
7066       if (LosslessReduceDepthOK(image,exception) != MagickFalse)
7067          image->depth = 8;
7068 #endif
7069
7070       if (image_info->number_scenes != 0)
7071         {
7072           if (mng_info->scenes_found >
7073              (ssize_t) (image_info->first_scene+image_info->number_scenes))
7074             break;
7075         }
7076
7077       if (logging != MagickFalse)
7078         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7079           "  Finished reading image datastream.");
7080
7081   } while (LocaleCompare(image_info->magick,"MNG") == 0);
7082
7083   (void) CloseBlob(image);
7084
7085   if (logging != MagickFalse)
7086     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7087       "  Finished reading all image datastreams.");
7088
7089 #if defined(MNG_INSERT_LAYERS)
7090   if (insert_layers && !mng_info->image_found && (mng_info->mng_width) &&
7091        (mng_info->mng_height))
7092     {
7093       /*
7094         Insert a background layer if nothing else was found.
7095       */
7096       if (logging != MagickFalse)
7097         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7098           "  No images found.  Inserting a background layer.");
7099
7100       if (GetAuthenticPixelQueue(image) != (Quantum *) NULL)
7101         {
7102           /*
7103             Allocate next image structure.
7104           */
7105           AcquireNextImage(image_info,image,exception);
7106           if (GetNextImageInList(image) == (Image *) NULL)
7107             {
7108               image=DestroyImageList(image);
7109               MngInfoFreeStruct(mng_info,&have_mng_structure);
7110
7111               if (logging != MagickFalse)
7112                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7113                   "  Allocation failed, returning NULL.");
7114
7115               return((Image *) NULL);
7116             }
7117           image=SyncNextImageInList(image);
7118         }
7119       image->columns=mng_info->mng_width;
7120       image->rows=mng_info->mng_height;
7121       image->page.width=mng_info->mng_width;
7122       image->page.height=mng_info->mng_height;
7123       image->page.x=0;
7124       image->page.y=0;
7125       image->background_color=mng_background_color;
7126       image->alpha_trait=UndefinedPixelTrait;
7127
7128       if (image_info->ping == MagickFalse)
7129         (void) SetImageBackgroundColor(image,exception);
7130
7131       mng_info->image_found++;
7132     }
7133 #endif
7134   image->iterations=mng_iterations;
7135
7136   if (mng_iterations == 1)
7137     image->start_loop=MagickTrue;
7138
7139   while (GetPreviousImageInList(image) != (Image *) NULL)
7140   {
7141     image_count++;
7142     if (image_count > 10*mng_info->image_found)
7143       {
7144         if (logging != MagickFalse)
7145           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  No beginning");
7146
7147         (void) ThrowMagickException(exception,GetMagickModule(),
7148           CoderError,"Linked list is corrupted, beginning of list not found",
7149           "`%s'",image_info->filename);
7150
7151         return((Image *) NULL);
7152       }
7153
7154     image=GetPreviousImageInList(image);
7155
7156     if (GetNextImageInList(image) == (Image *) NULL)
7157       {
7158         if (logging != MagickFalse)
7159           (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Corrupt list");
7160
7161         (void) ThrowMagickException(exception,GetMagickModule(),
7162           CoderError,"Linked list is corrupted; next_image is NULL","`%s'",
7163           image_info->filename);
7164       }
7165   }
7166
7167   if (mng_info->ticks_per_second && mng_info->image_found > 1 &&
7168              GetNextImageInList(image) ==
7169      (Image *) NULL)
7170     {
7171       if (logging != MagickFalse)
7172         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7173             "  First image null");
7174
7175       (void) ThrowMagickException(exception,GetMagickModule(),
7176         CoderError,"image->next for first image is NULL but shouldn't be.",
7177         "`%s'",image_info->filename);
7178     }
7179
7180   if (mng_info->image_found == 0)
7181     {
7182       if (logging != MagickFalse)
7183         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7184           "  No visible images found.");
7185
7186       (void) ThrowMagickException(exception,GetMagickModule(),
7187         CoderError,"No visible images in file","`%s'",image_info->filename);
7188
7189       if (image != (Image *) NULL)
7190         image=DestroyImageList(image);
7191
7192       MngInfoFreeStruct(mng_info,&have_mng_structure);
7193       return((Image *) NULL);
7194     }
7195
7196   if (mng_info->ticks_per_second)
7197     final_delay=1UL*MagickMax(image->ticks_per_second,1L)*
7198             final_delay/mng_info->ticks_per_second;
7199
7200   else
7201     image->start_loop=MagickTrue;
7202
7203   /* Find final nonzero image delay */
7204   final_image_delay=0;
7205
7206   while (GetNextImageInList(image) != (Image *) NULL)
7207     {
7208       if (image->delay)
7209         final_image_delay=image->delay;
7210
7211       image=GetNextImageInList(image);
7212     }
7213
7214   if (final_delay < final_image_delay)
7215     final_delay=final_image_delay;
7216
7217   image->delay=final_delay;
7218
7219   if (logging != MagickFalse)
7220       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7221         "  image->delay=%.20g, final_delay=%.20g",(double) image->delay,
7222         (double) final_delay);
7223
7224   if (logging != MagickFalse)
7225     {
7226       int
7227         scene;
7228
7229       scene=0;
7230       image=GetFirstImageInList(image);
7231
7232       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7233         "  Before coalesce:");
7234
7235       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7236         "    scene 0 delay=%.20g",(double) image->delay);
7237
7238       while (GetNextImageInList(image) != (Image *) NULL)
7239       {
7240         image=GetNextImageInList(image);
7241         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7242           "    scene %.20g delay=%.20g",(double) scene++,(double) image->delay);
7243       }
7244     }
7245
7246   image=GetFirstImageInList(image);
7247 #ifdef MNG_COALESCE_LAYERS
7248   if (insert_layers)
7249     {
7250       Image
7251         *next_image,
7252         *next;
7253
7254       size_t
7255         scene;
7256
7257       if (logging != MagickFalse)
7258         (void) LogMagickEvent(CoderEvent,GetMagickModule(),"  Coalesce Images");
7259
7260       scene=image->scene;
7261       next_image=CoalesceImages(image,exception);
7262
7263       if (next_image == (Image *) NULL)
7264         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
7265
7266       image=DestroyImageList(image);
7267       image=next_image;
7268
7269       for (next=image; next != (Image *) NULL; next=next_image)
7270       {
7271          next->page.width=mng_info->mng_width;
7272          next->page.height=mng_info->mng_height;
7273          next->page.x=0;
7274          next->page.y=0;
7275          next->scene=scene++;
7276          next_image=GetNextImageInList(next);
7277
7278          if (next_image == (Image *) NULL)
7279            break;
7280
7281          if (next->delay == 0)
7282            {
7283              scene--;
7284              next_image->previous=GetPreviousImageInList(next);
7285              if (GetPreviousImageInList(next) == (Image *) NULL)
7286                image=next_image;
7287              else
7288                next->previous->next=next_image;
7289              next=DestroyImage(next);
7290            }
7291       }
7292     }
7293 #endif
7294
7295   while (GetNextImageInList(image) != (Image *) NULL)
7296       image=GetNextImageInList(image);
7297
7298   image->dispose=BackgroundDispose;
7299
7300   if (logging != MagickFalse)
7301     {
7302       int
7303         scene;
7304
7305       scene=0;
7306       image=GetFirstImageInList(image);
7307
7308       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7309         "  After coalesce:");
7310
7311       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7312         "    scene 0 delay=%.20g dispose=%.20g",(double) image->delay,
7313         (double) image->dispose);
7314
7315       while (GetNextImageInList(image) != (Image *) NULL)
7316       {
7317         image=GetNextImageInList(image);
7318
7319         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7320           "    scene %.20g delay=%.20g dispose=%.20g",(double) scene++,
7321           (double) image->delay,(double) image->dispose);
7322       }
7323    }
7324
7325   image=GetFirstImageInList(image);
7326   MngInfoFreeStruct(mng_info,&have_mng_structure);
7327   have_mng_structure=MagickFalse;
7328
7329   if (logging != MagickFalse)
7330     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit ReadMNGImage()");
7331
7332   return(GetFirstImageInList(image));
7333 }
7334 #else /* PNG_LIBPNG_VER > 10011 */
7335 static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7336 {
7337   printf("Your PNG library is too old: You have libpng-%s\n",
7338      PNG_LIBPNG_VER_STRING);
7339
7340   (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7341     "PNG library is too old","`%s'",image_info->filename);
7342
7343   return(Image *) NULL;
7344 }
7345
7346 static Image *ReadMNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
7347 {
7348   return(ReadPNGImage(image_info,exception));
7349 }
7350 #endif /* PNG_LIBPNG_VER > 10011 */
7351 #endif
7352 \f
7353 /*
7354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7355 %                                                                             %
7356 %                                                                             %
7357 %                                                                             %
7358 %   R e g i s t e r P N G I m a g e                                           %
7359 %                                                                             %
7360 %                                                                             %
7361 %                                                                             %
7362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7363 %
7364 %  RegisterPNGImage() adds properties for the PNG image format to
7365 %  the list of supported formats.  The properties include the image format
7366 %  tag, a method to read and/or write the format, whether the format
7367 %  supports the saving of more than one frame to the same file or blob,
7368 %  whether the format supports native in-memory I/O, and a brief
7369 %  description of the format.
7370 %
7371 %  The format of the RegisterPNGImage method is:
7372 %
7373 %      size_t RegisterPNGImage(void)
7374 %
7375 */
7376 ModuleExport size_t RegisterPNGImage(void)
7377 {
7378   char
7379     version[MagickPathExtent];
7380
7381   MagickInfo
7382     *entry;
7383
7384   static const char
7385     *PNGNote=
7386     {
7387       "See http://www.libpng.org/ for details about the PNG format."
7388     },
7389
7390     *JNGNote=
7391     {
7392       "See http://www.libpng.org/pub/mng/ for details about the JNG\n"
7393       "format."
7394     },
7395
7396     *MNGNote=
7397     {
7398       "See http://www.libpng.org/pub/mng/ for details about the MNG\n"
7399       "format."
7400     };
7401
7402   *version='\0';
7403
7404 #if defined(PNG_LIBPNG_VER_STRING)
7405   (void) ConcatenateMagickString(version,"libpng ",MagickPathExtent);
7406   (void) ConcatenateMagickString(version,PNG_LIBPNG_VER_STRING,MagickPathExtent);
7407
7408   if (LocaleCompare(PNG_LIBPNG_VER_STRING,png_get_header_ver(NULL)) != 0)
7409     {
7410       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7411       (void) ConcatenateMagickString(version,png_get_libpng_ver(NULL),
7412             MagickPathExtent);
7413     }
7414 #endif
7415
7416   entry=AcquireMagickInfo("PNG","MNG","Multiple-image Network Graphics");
7417   entry->flags|=CoderSeekableStreamFlag;  /* To do: eliminate this. */
7418
7419 #if defined(MAGICKCORE_PNG_DELEGATE)
7420   entry->decoder=(DecodeImageHandler *) ReadMNGImage;
7421   entry->encoder=(EncodeImageHandler *) WriteMNGImage;
7422 #endif
7423
7424   entry->magick=(IsImageFormatHandler *) IsMNG;
7425
7426   if (*version != '\0')
7427     entry->version=ConstantString(version);
7428
7429   entry->mime_type=ConstantString("video/x-mng");
7430   entry->note=ConstantString(MNGNote);
7431   (void) RegisterMagickInfo(entry);
7432
7433   entry=AcquireMagickInfo("PNG","PNG","Portable Network Graphics");
7434
7435 #if defined(MAGICKCORE_PNG_DELEGATE)
7436   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7437   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7438 #endif
7439
7440   entry->magick=(IsImageFormatHandler *) IsPNG;
7441   entry->flags^=CoderAdjoinFlag;
7442   entry->mime_type=ConstantString("image/png");
7443
7444   if (*version != '\0')
7445     entry->version=ConstantString(version);
7446
7447   entry->note=ConstantString(PNGNote);
7448   (void) RegisterMagickInfo(entry);
7449
7450   entry=AcquireMagickInfo("PNG","PNG8",
7451     "8-bit indexed with optional binary transparency");
7452
7453 #if defined(MAGICKCORE_PNG_DELEGATE)
7454   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7455   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7456 #endif
7457
7458   entry->magick=(IsImageFormatHandler *) IsPNG;
7459   entry->flags^=CoderAdjoinFlag;
7460   entry->mime_type=ConstantString("image/png");
7461   (void) RegisterMagickInfo(entry);
7462
7463   entry=AcquireMagickInfo("PNG","PNG24",
7464     "opaque or binary transparent 24-bit RGB");
7465   *version='\0';
7466
7467 #if defined(ZLIB_VERSION)
7468   (void) ConcatenateMagickString(version,"zlib ",MagickPathExtent);
7469   (void) ConcatenateMagickString(version,ZLIB_VERSION,MagickPathExtent);
7470
7471   if (LocaleCompare(ZLIB_VERSION,zlib_version) != 0)
7472     {
7473       (void) ConcatenateMagickString(version,",",MagickPathExtent);
7474       (void) ConcatenateMagickString(version,zlib_version,MagickPathExtent);
7475     }
7476 #endif
7477
7478   if (*version != '\0')
7479     entry->version=ConstantString(version);
7480
7481 #if defined(MAGICKCORE_PNG_DELEGATE)
7482   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7483   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7484 #endif
7485
7486   entry->magick=(IsImageFormatHandler *) IsPNG;
7487   entry->flags^=CoderAdjoinFlag;
7488   entry->mime_type=ConstantString("image/png");
7489   (void) RegisterMagickInfo(entry);
7490
7491   entry=AcquireMagickInfo("PNG","PNG32","opaque or transparent 32-bit RGBA");
7492
7493 #if defined(MAGICKCORE_PNG_DELEGATE)
7494   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7495   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7496 #endif
7497
7498   entry->magick=(IsImageFormatHandler *) IsPNG;
7499   entry->flags^=CoderAdjoinFlag;
7500   entry->mime_type=ConstantString("image/png");
7501   (void) RegisterMagickInfo(entry);
7502
7503   entry=AcquireMagickInfo("PNG","PNG48",
7504     "opaque or binary transparent 48-bit RGB");
7505
7506 #if defined(MAGICKCORE_PNG_DELEGATE)
7507   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7508   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7509 #endif
7510
7511   entry->magick=(IsImageFormatHandler *) IsPNG;
7512   entry->flags^=CoderAdjoinFlag;
7513   entry->mime_type=ConstantString("image/png");
7514   (void) RegisterMagickInfo(entry);
7515
7516   entry=AcquireMagickInfo("PNG","PNG64","opaque or transparent 64-bit RGBA");
7517
7518 #if defined(MAGICKCORE_PNG_DELEGATE)
7519   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7520   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7521 #endif
7522
7523   entry->magick=(IsImageFormatHandler *) IsPNG;
7524   entry->flags^=CoderAdjoinFlag;
7525   entry->mime_type=ConstantString("image/png");
7526   (void) RegisterMagickInfo(entry);
7527
7528   entry=AcquireMagickInfo("PNG","PNG00",
7529     "PNG inheriting bit-depth, color-type from original, if possible");
7530
7531 #if defined(MAGICKCORE_PNG_DELEGATE)
7532   entry->decoder=(DecodeImageHandler *) ReadPNGImage;
7533   entry->encoder=(EncodeImageHandler *) WritePNGImage;
7534 #endif
7535
7536   entry->magick=(IsImageFormatHandler *) IsPNG;
7537   entry->flags^=CoderAdjoinFlag;
7538   entry->mime_type=ConstantString("image/png");
7539   (void) RegisterMagickInfo(entry);
7540
7541   entry=AcquireMagickInfo("PNG","JNG","JPEG Network Graphics");
7542
7543 #if defined(JNG_SUPPORTED)
7544 #if defined(MAGICKCORE_PNG_DELEGATE)
7545   entry->decoder=(DecodeImageHandler *) ReadJNGImage;
7546   entry->encoder=(EncodeImageHandler *) WriteJNGImage;
7547 #endif
7548 #endif
7549
7550   entry->magick=(IsImageFormatHandler *) IsJNG;
7551   entry->flags^=CoderAdjoinFlag;
7552   entry->mime_type=ConstantString("image/x-jng");
7553   entry->note=ConstantString(JNGNote);
7554   (void) RegisterMagickInfo(entry);
7555
7556 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7557   ping_semaphore=AcquireSemaphoreInfo();
7558 #endif
7559
7560   return(MagickImageCoderSignature);
7561 }
7562 \f
7563 /*
7564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7565 %                                                                             %
7566 %                                                                             %
7567 %                                                                             %
7568 %   U n r e g i s t e r P N G I m a g e                                       %
7569 %                                                                             %
7570 %                                                                             %
7571 %                                                                             %
7572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7573 %
7574 %  UnregisterPNGImage() removes format registrations made by the
7575 %  PNG module from the list of supported formats.
7576 %
7577 %  The format of the UnregisterPNGImage method is:
7578 %
7579 %      UnregisterPNGImage(void)
7580 %
7581 */
7582 ModuleExport void UnregisterPNGImage(void)
7583 {
7584   (void) UnregisterMagickInfo("MNG");
7585   (void) UnregisterMagickInfo("PNG");
7586   (void) UnregisterMagickInfo("PNG8");
7587   (void) UnregisterMagickInfo("PNG24");
7588   (void) UnregisterMagickInfo("PNG32");
7589   (void) UnregisterMagickInfo("PNG48");
7590   (void) UnregisterMagickInfo("PNG64");
7591   (void) UnregisterMagickInfo("PNG00");
7592   (void) UnregisterMagickInfo("JNG");
7593
7594 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
7595   if (ping_semaphore != (SemaphoreInfo *) NULL)
7596     RelinquishSemaphoreInfo(&ping_semaphore);
7597 #endif
7598 }
7599 \f
7600 #if defined(MAGICKCORE_PNG_DELEGATE)
7601 #if PNG_LIBPNG_VER > 10011
7602 /*
7603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7604 %                                                                             %
7605 %                                                                             %
7606 %                                                                             %
7607 %   W r i t e M N G I m a g e                                                 %
7608 %                                                                             %
7609 %                                                                             %
7610 %                                                                             %
7611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7612 %
7613 %  WriteMNGImage() writes an image in the Portable Network Graphics
7614 %  Group's "Multiple-image Network Graphics" encoded image format.
7615 %
7616 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
7617 %
7618 %  The format of the WriteMNGImage method is:
7619 %
7620 %      MagickBooleanType WriteMNGImage(const ImageInfo *image_info,
7621 %        Image *image,ExceptionInfo *exception)
7622 %
7623 %  A description of each parameter follows.
7624 %
7625 %    o image_info: the image info.
7626 %
7627 %    o image:  The image.
7628 %
7629 %    o exception: return any errors or warnings in this structure.
7630 %
7631 %  To do (as of version 5.5.2, November 26, 2002 -- glennrp -- see also
7632 %    "To do" under ReadPNGImage):
7633 %
7634 %    Preserve all unknown and not-yet-handled known chunks found in input
7635 %    PNG file and copy them  into output PNG files according to the PNG
7636 %    copying rules.
7637 %
7638 %    Write the iCCP chunk at MNG level when (icc profile length > 0)
7639 %
7640 %    Improve selection of color type (use indexed-colour or indexed-colour
7641 %    with tRNS when 256 or fewer unique RGBA values are present).
7642 %
7643 %    Figure out what to do with "dispose=<restore-to-previous>" (dispose == 3)
7644 %    This will be complicated if we limit ourselves to generating MNG-LC
7645 %    files.  For now we ignore disposal method 3 and simply overlay the next
7646 %    image on it.
7647 %
7648 %    Check for identical PLTE's or PLTE/tRNS combinations and use a
7649 %    global MNG PLTE or PLTE/tRNS combination when appropriate.
7650 %    [mostly done 15 June 1999 but still need to take care of tRNS]
7651 %
7652 %    Check for identical sRGB and replace with a global sRGB (and remove
7653 %    gAMA/cHRM if sRGB is found; check for identical gAMA/cHRM and
7654 %    replace with global gAMA/cHRM (or with sRGB if appropriate; replace
7655 %    local gAMA/cHRM with local sRGB if appropriate).
7656 %
7657 %    Check for identical sBIT chunks and write global ones.
7658 %
7659 %    Provide option to skip writing the signature tEXt chunks.
7660 %
7661 %    Use signatures to detect identical objects and reuse the first
7662 %    instance of such objects instead of writing duplicate objects.
7663 %
7664 %    Use a smaller-than-32k value of compression window size when
7665 %    appropriate.
7666 %
7667 %    Encode JNG datastreams.  Mostly done as of 5.5.2; need to write
7668 %    ancillary text chunks and save profiles.
7669 %
7670 %    Provide an option to force LC files (to ensure exact framing rate)
7671 %    instead of VLC.
7672 %
7673 %    Provide an option to force VLC files instead of LC, even when offsets
7674 %    are present.  This will involve expanding the embedded images with a
7675 %    transparent region at the top and/or left.
7676 */
7677
7678 static void
7679 Magick_png_write_raw_profile(const ImageInfo *image_info,png_struct *ping,
7680    png_info *ping_info, unsigned char *profile_type, unsigned char
7681    *profile_description, unsigned char *profile_data, png_uint_32 length)
7682 {
7683    png_textp
7684      text;
7685
7686    register ssize_t
7687      i;
7688
7689    unsigned char
7690      *sp;
7691
7692    png_charp
7693      dp;
7694
7695    png_uint_32
7696      allocated_length,
7697      description_length;
7698
7699    unsigned char
7700      hex[16]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
7701
7702    if (LocaleNCompare((char *) profile_type+1, "ng-chunk-",9) == 0)
7703       return;
7704
7705    if (image_info->verbose)
7706      {
7707        (void) printf("writing raw profile: type=%s, length=%.20g\n",
7708          (char *) profile_type, (double) length);
7709      }
7710
7711 #if PNG_LIBPNG_VER >= 10400
7712    text=(png_textp) png_malloc(ping,(png_alloc_size_t) sizeof(png_text));
7713 #else
7714    text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
7715 #endif
7716    description_length=(png_uint_32) strlen((const char *) profile_description);
7717    allocated_length=(png_uint_32) (length*2 + (length >> 5) + 20
7718       + description_length);
7719 #if PNG_LIBPNG_VER >= 10400
7720    text[0].text=(png_charp) png_malloc(ping,
7721       (png_alloc_size_t) allocated_length);
7722    text[0].key=(png_charp) png_malloc(ping, (png_alloc_size_t) 80);
7723 #else
7724    text[0].text=(png_charp) png_malloc(ping, (png_size_t) allocated_length);
7725    text[0].key=(png_charp) png_malloc(ping, (png_size_t) 80);
7726 #endif
7727    text[0].key[0]='\0';
7728    (void) ConcatenateMagickString(text[0].key,
7729       "Raw profile type ",MagickPathExtent);
7730    (void) ConcatenateMagickString(text[0].key,(const char *) profile_type,62);
7731    sp=profile_data;
7732    dp=text[0].text;
7733    *dp++='\n';
7734    (void) CopyMagickString(dp,(const char *) profile_description,
7735      allocated_length);
7736    dp+=description_length;
7737    *dp++='\n';
7738    (void) FormatLocaleString(dp,allocated_length-
7739      (png_size_t) (dp-text[0].text),"%8lu ",(unsigned long) length);
7740    dp+=8;
7741
7742    for (i=0; i < (ssize_t) length; i++)
7743    {
7744      if (i%36 == 0)
7745        *dp++='\n';
7746      *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
7747      *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
7748    }
7749
7750    *dp++='\n';
7751    *dp='\0';
7752    text[0].text_length=(png_size_t) (dp-text[0].text);
7753    text[0].compression=image_info->compression == NoCompression ||
7754      (image_info->compression == UndefinedCompression &&
7755      text[0].text_length < 128) ? -1 : 0;
7756
7757    if (text[0].text_length <= allocated_length)
7758      png_set_text(ping,ping_info,text,1);
7759
7760    png_free(ping,text[0].text);
7761    png_free(ping,text[0].key);
7762    png_free(ping,text);
7763 }
7764
7765 static MagickBooleanType Magick_png_write_chunk_from_profile(Image *image,
7766   const char *string, MagickBooleanType logging)
7767 {
7768   char
7769     *name;
7770
7771   const StringInfo
7772     *profile;
7773
7774   unsigned char
7775     *data;
7776
7777   png_uint_32 length;
7778
7779   ResetImageProfileIterator(image);
7780
7781   for (name=GetNextImageProfile(image); name != (const char *) NULL; )
7782   {
7783     profile=GetImageProfile(image,name);
7784
7785     if (profile != (const StringInfo *) NULL)
7786       {
7787         StringInfo
7788           *ping_profile;
7789
7790         if (LocaleNCompare(name,string,11) == 0)
7791           {
7792             if (logging != MagickFalse)
7793                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
7794                    "  Found %s profile",name);
7795
7796             ping_profile=CloneStringInfo(profile);
7797             data=GetStringInfoDatum(ping_profile),
7798             length=(png_uint_32) GetStringInfoLength(ping_profile);
7799             data[4]=data[3];
7800             data[3]=data[2];
7801             data[2]=data[1];
7802             data[1]=data[0];
7803             (void) WriteBlobMSBULong(image,length-5);  /* data length */
7804             (void) WriteBlob(image,length-1,data+1);
7805             (void) WriteBlobMSBULong(image,crc32(0,data+1,(uInt) length-1));
7806             ping_profile=DestroyStringInfo(ping_profile);
7807           }
7808       }
7809
7810       name=GetNextImageProfile(image);
7811    }
7812
7813    return(MagickTrue);
7814 }
7815
7816 static inline MagickBooleanType Magick_png_color_equal(const Image *image,
7817   const Quantum *p, const PixelInfo *q)
7818 {
7819   MagickRealType
7820     value;
7821
7822   value=(MagickRealType) p[image->channel_map[RedPixelChannel].offset];
7823   if (AbsolutePixelValue(value-q->red) >= MagickEpsilon)
7824     return(MagickFalse);
7825   value=(MagickRealType) p[image->channel_map[GreenPixelChannel].offset];
7826   if (AbsolutePixelValue(value-q->green) >= MagickEpsilon)
7827     return(MagickFalse);
7828   value=(MagickRealType) p[image->channel_map[BluePixelChannel].offset];
7829   if (AbsolutePixelValue(value-q->blue) >= MagickEpsilon)
7830     return(MagickFalse);
7831
7832   return(MagickTrue);
7833 }
7834
7835 #if defined(PNG_tIME_SUPPORTED)
7836 static void write_tIME_chunk(Image *image,png_struct *ping,png_info *info,
7837   const char *date,ExceptionInfo *exception)
7838 {
7839   unsigned int
7840     day,
7841     hour,
7842     minute,
7843     month,
7844     second,
7845     year;
7846
7847   png_time
7848     ptime;
7849
7850   time_t
7851     ttime;
7852
7853   if (date != (const char *) NULL)
7854     {
7855       if (sscanf(date,"%d-%d-%dT%d:%d:%dZ",&year,&month,&day,&hour,&minute,
7856           &second) != 6)
7857         {
7858           (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
7859             "Invalid date format specified for png:tIME","`%s'",
7860             image->filename);
7861           return;
7862         }
7863       ptime.year=(png_uint_16) year;
7864       ptime.month=(png_byte) month;
7865       ptime.day=(png_byte) day;
7866       ptime.hour=(png_byte) hour;
7867       ptime.minute=(png_byte) minute;
7868       ptime.second=(png_byte) second;
7869     }
7870   else
7871   {
7872     time(&ttime);
7873     png_convert_from_time_t(&ptime,ttime);
7874   }
7875   png_set_tIME(ping,info,&ptime);
7876 }
7877 #endif
7878
7879 /* Write one PNG image */
7880 static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
7881   const ImageInfo *IMimage_info,Image *IMimage,ExceptionInfo *exception)
7882 {
7883   char
7884     im_vers[32],
7885     libpng_runv[32],
7886     libpng_vers[32],
7887     zlib_runv[32],
7888     zlib_vers[32];
7889
7890   Image
7891     *image;
7892
7893   ImageInfo
7894     *image_info;
7895
7896   char
7897     s[2];
7898
7899   const char
7900     *name,
7901     *property,
7902     *value;
7903
7904   const StringInfo
7905     *profile;
7906
7907   int
7908     num_passes,
7909     pass;
7910
7911   png_byte
7912      ping_trans_alpha[256];
7913
7914   png_color
7915      palette[257];
7916
7917   png_color_16
7918     ping_background,
7919     ping_trans_color;
7920
7921   png_info
7922     *ping_info;
7923
7924   png_struct
7925     *ping;
7926
7927   png_uint_32
7928     ping_height,
7929     ping_width;
7930
7931   ssize_t
7932     y;
7933
7934   MagickBooleanType
7935     image_matte,
7936     logging,
7937     matte,
7938
7939     ping_have_blob,
7940     ping_have_cheap_transparency,
7941     ping_have_color,
7942     ping_have_non_bw,
7943     ping_have_PLTE,
7944     ping_have_bKGD,
7945     ping_have_iCCP,
7946     ping_have_pHYs,
7947     ping_have_sRGB,
7948     ping_have_tRNS,
7949
7950     ping_exclude_bKGD,
7951     ping_exclude_cHRM,
7952     ping_exclude_date,
7953     /* ping_exclude_EXIF, */
7954     ping_exclude_gAMA,
7955     ping_exclude_iCCP,
7956     /* ping_exclude_iTXt, */
7957     ping_exclude_oFFs,
7958     ping_exclude_pHYs,
7959     ping_exclude_sRGB,
7960     ping_exclude_tEXt,
7961     ping_exclude_tIME,
7962     /* ping_exclude_tRNS, */
7963     ping_exclude_vpAg,
7964     ping_exclude_zCCP, /* hex-encoded iCCP */
7965     ping_exclude_zTXt,
7966
7967     ping_preserve_colormap,
7968     ping_preserve_iCCP,
7969     ping_need_colortype_warning,
7970
7971     status,
7972     tried_332,
7973     tried_333,
7974     tried_444;
7975
7976   MemoryInfo
7977     *volatile pixel_info;
7978
7979   QuantumInfo
7980     *quantum_info;
7981
7982   PNGErrorInfo
7983     error_info;
7984
7985   register ssize_t
7986     i,
7987     x;
7988
7989   unsigned char
7990     *ping_pixels;
7991
7992   volatile int
7993     image_colors,
7994     ping_bit_depth,
7995     ping_color_type,
7996     ping_interlace_method,
7997     ping_compression_method,
7998     ping_filter_method,
7999     ping_num_trans;
8000
8001   volatile size_t
8002     image_depth,
8003     old_bit_depth;
8004
8005   size_t
8006     quality,
8007     rowbytes,
8008     save_image_depth;
8009
8010   int
8011     j,
8012     number_colors,
8013     number_opaque,
8014     number_semitransparent,
8015     number_transparent,
8016     ping_pHYs_unit_type;
8017
8018   png_uint_32
8019     ping_pHYs_x_resolution,
8020     ping_pHYs_y_resolution;
8021
8022   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
8023     "  Enter WriteOnePNGImage()");
8024
8025   image = CloneImage(IMimage,0,0,MagickFalse,exception);
8026   image_info=(ImageInfo *) CloneImageInfo(IMimage_info);
8027   if (image_info == (ImageInfo *) NULL)
8028      ThrowWriterException(ResourceLimitError, "MemoryAllocationFailed");
8029
8030   /* Define these outside of the following "if logging()" block so they will
8031    * show in debuggers.
8032    */
8033   *im_vers='\0';
8034   (void) ConcatenateMagickString(im_vers,
8035          MagickLibVersionText,MagickPathExtent);
8036   (void) ConcatenateMagickString(im_vers,
8037          MagickLibAddendum,MagickPathExtent);
8038
8039   *libpng_vers='\0';
8040   (void) ConcatenateMagickString(libpng_vers,
8041          PNG_LIBPNG_VER_STRING,32);
8042   *libpng_runv='\0';
8043   (void) ConcatenateMagickString(libpng_runv,
8044          png_get_libpng_ver(NULL),32);
8045
8046   *zlib_vers='\0';
8047   (void) ConcatenateMagickString(zlib_vers,
8048          ZLIB_VERSION,32);
8049   *zlib_runv='\0';
8050   (void) ConcatenateMagickString(zlib_runv,
8051          zlib_version,32);
8052
8053   if (logging != MagickFalse)
8054     {
8055        LogMagickEvent(CoderEvent,GetMagickModule(),"    IM version     = %s",
8056            im_vers);
8057        LogMagickEvent(CoderEvent,GetMagickModule(),"    Libpng version = %s",
8058            libpng_vers);
8059        if (LocaleCompare(libpng_vers,libpng_runv) != 0)
8060        {
8061        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8062            libpng_runv);
8063        }
8064        LogMagickEvent(CoderEvent,GetMagickModule(),"    Zlib version   = %s",
8065            zlib_vers);
8066        if (LocaleCompare(zlib_vers,zlib_runv) != 0)
8067        {
8068        LogMagickEvent(CoderEvent,GetMagickModule(),"      running with   %s",
8069            zlib_runv);
8070        }
8071     }
8072
8073   /* Initialize some stuff */
8074   ping_bit_depth=0,
8075   ping_color_type=0,
8076   ping_interlace_method=0,
8077   ping_compression_method=0,
8078   ping_filter_method=0,
8079   ping_num_trans = 0;
8080
8081   ping_background.red = 0;
8082   ping_background.green = 0;
8083   ping_background.blue = 0;
8084   ping_background.gray = 0;
8085   ping_background.index = 0;
8086
8087   ping_trans_color.red=0;
8088   ping_trans_color.green=0;
8089   ping_trans_color.blue=0;
8090   ping_trans_color.gray=0;
8091
8092   ping_pHYs_unit_type = 0;
8093   ping_pHYs_x_resolution = 0;
8094   ping_pHYs_y_resolution = 0;
8095
8096   ping_have_blob=MagickFalse;
8097   ping_have_cheap_transparency=MagickFalse;
8098   ping_have_color=MagickTrue;
8099   ping_have_non_bw=MagickTrue;
8100   ping_have_PLTE=MagickFalse;
8101   ping_have_bKGD=MagickFalse;
8102   ping_have_iCCP=MagickFalse;
8103   ping_have_pHYs=MagickFalse;
8104   ping_have_sRGB=MagickFalse;
8105   ping_have_tRNS=MagickFalse;
8106
8107   ping_exclude_bKGD=mng_info->ping_exclude_bKGD;
8108   ping_exclude_cHRM=mng_info->ping_exclude_cHRM;
8109   ping_exclude_date=mng_info->ping_exclude_date;
8110   /* ping_exclude_EXIF=mng_info->ping_exclude_EXIF; */
8111   ping_exclude_gAMA=mng_info->ping_exclude_gAMA;
8112   ping_exclude_iCCP=mng_info->ping_exclude_iCCP;
8113   /* ping_exclude_iTXt=mng_info->ping_exclude_iTXt; */
8114   ping_exclude_oFFs=mng_info->ping_exclude_oFFs;
8115   ping_exclude_pHYs=mng_info->ping_exclude_pHYs;
8116   ping_exclude_sRGB=mng_info->ping_exclude_sRGB;
8117   ping_exclude_tEXt=mng_info->ping_exclude_tEXt;
8118   ping_exclude_tIME=mng_info->ping_exclude_tIME;
8119   /* ping_exclude_tRNS=mng_info->ping_exclude_tRNS; */
8120   ping_exclude_vpAg=mng_info->ping_exclude_vpAg;
8121   ping_exclude_zCCP=mng_info->ping_exclude_zCCP; /* hex-encoded iCCP in zTXt */
8122   ping_exclude_zTXt=mng_info->ping_exclude_zTXt;
8123
8124   ping_preserve_colormap = mng_info->ping_preserve_colormap;
8125   ping_preserve_iCCP = mng_info->ping_preserve_iCCP;
8126   ping_need_colortype_warning = MagickFalse;
8127
8128   /* Recognize the ICC sRGB profile and convert it to the sRGB chunk,
8129    * i.e., eliminate the ICC profile and set image->rendering_intent.
8130    * Note that this will not involve any changes to the actual pixels
8131    * but merely passes information to applications that read the resulting
8132    * PNG image.
8133    *
8134    * To do: recognize other variants of the sRGB profile, using the CRC to
8135    * verify all recognized variants including the 7 already known.
8136    *
8137    * Work around libpng16+ rejecting some "known invalid sRGB profiles".
8138    *
8139    * Use something other than image->rendering_intent to record the fact
8140    * that the sRGB profile was found.
8141    *
8142    * Record the ICC version (currently v2 or v4) of the incoming sRGB ICC
8143    * profile.  Record the Blackpoint Compensation, if any.
8144    */
8145    if (ping_exclude_sRGB == MagickFalse && ping_preserve_iCCP == MagickFalse)
8146    {
8147       char
8148         *name;
8149
8150       const StringInfo
8151         *profile;
8152
8153       ResetImageProfileIterator(image);
8154       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
8155       {
8156         profile=GetImageProfile(image,name);
8157
8158         if (profile != (StringInfo *) NULL)
8159           {
8160             if ((LocaleCompare(name,"ICC") == 0) ||
8161                 (LocaleCompare(name,"ICM") == 0))
8162
8163              {
8164                  int
8165                    icheck,
8166                    got_crc=0;
8167
8168
8169                  png_uint_32
8170                    length,
8171                    profile_crc=0;
8172
8173                  unsigned char
8174                    *data;
8175
8176                  length=(png_uint_32) GetStringInfoLength(profile);
8177
8178                  for (icheck=0; sRGB_info[icheck].len > 0; icheck++)
8179                  {
8180                    if (length == sRGB_info[icheck].len)
8181                    {
8182                      if (got_crc == 0)
8183                      {
8184                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8185                          "    Got a %lu-byte ICC profile (potentially sRGB)",
8186                          (unsigned long) length);
8187
8188                        data=GetStringInfoDatum(profile);
8189                        profile_crc=crc32(0,data,length);
8190
8191                        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8192                            "      with crc=%8x",(unsigned int) profile_crc);
8193                        got_crc++;
8194                      }
8195
8196                      if (profile_crc == sRGB_info[icheck].crc)
8197                      {
8198                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8199                             "      It is sRGB with rendering intent = %s",
8200                         Magick_RenderingIntentString_from_PNG_RenderingIntent(
8201                              sRGB_info[icheck].intent));
8202                         if (image->rendering_intent==UndefinedIntent)
8203                         {
8204                           image->rendering_intent=
8205                           Magick_RenderingIntent_from_PNG_RenderingIntent(
8206                              sRGB_info[icheck].intent);
8207                         }
8208                         ping_exclude_iCCP = MagickTrue;
8209                         ping_exclude_zCCP = MagickTrue;
8210                         ping_have_sRGB = MagickTrue;
8211                         break;
8212                      }
8213                    }
8214                  }
8215                  if (sRGB_info[icheck].len == 0)
8216                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8217                         "    Got a %lu-byte ICC profile not recognized as sRGB",
8218                         (unsigned long) length);
8219               }
8220           }
8221         name=GetNextImageProfile(image);
8222       }
8223   }
8224
8225   number_opaque = 0;
8226   number_semitransparent = 0;
8227   number_transparent = 0;
8228
8229   if (logging != MagickFalse)
8230     {
8231       if (image->storage_class == UndefinedClass)
8232           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8233           "    image->storage_class=UndefinedClass");
8234       if (image->storage_class == DirectClass)
8235           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8236           "    image->storage_class=DirectClass");
8237       if (image->storage_class == PseudoClass)
8238           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8239           "    image->storage_class=PseudoClass");
8240       (void) LogMagickEvent(CoderEvent,GetMagickModule(), image->taint ?
8241           "    image->taint=MagickTrue":
8242           "    image->taint=MagickFalse");
8243     }
8244
8245   if (image->storage_class == PseudoClass &&
8246      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
8247      mng_info->write_png48 || mng_info->write_png64 ||
8248      (mng_info->write_png_colortype != 1 &&
8249      mng_info->write_png_colortype != 5)))
8250     {
8251       (void) SyncImage(image,exception);
8252       image->storage_class = DirectClass;
8253     }
8254
8255   if (ping_preserve_colormap == MagickFalse)
8256     {
8257       if (image->storage_class != PseudoClass && image->colormap != NULL)
8258         {
8259           /* Free the bogus colormap; it can cause trouble later */
8260            if (logging != MagickFalse)
8261               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8262               "    Freeing bogus colormap");
8263            (void) RelinquishMagickMemory(image->colormap);
8264            image->colormap=NULL;
8265         }
8266     }
8267
8268   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8269     (void) TransformImageColorspace(image,sRGBColorspace,exception);
8270
8271   /*
8272     Sometimes we get PseudoClass images whose RGB values don't match
8273     the colors in the colormap.  This code syncs the RGB values.
8274   */
8275   if (image->depth <= 8 && image->taint && image->storage_class == PseudoClass)
8276      (void) SyncImage(image,exception);
8277
8278 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
8279   if (image->depth > 8)
8280     {
8281       if (logging != MagickFalse)
8282         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8283           "    Reducing PNG bit depth to 8 since this is a Q8 build.");
8284
8285       image->depth=8;
8286     }
8287 #endif
8288
8289   /* Respect the -depth option */
8290   if (image->depth < 4)
8291     {
8292        register Quantum
8293          *r;
8294
8295        if (image->depth > 2)
8296          {
8297            /* Scale to 4-bit */
8298            LBR04PacketRGBO(image->background_color);
8299
8300            for (y=0; y < (ssize_t) image->rows; y++)
8301            {
8302              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8303
8304              if (r == (Quantum *) NULL)
8305                break;
8306
8307              for (x=0; x < (ssize_t) image->columns; x++)
8308              {
8309                 LBR04PixelRGBA(r);
8310                 r+=GetPixelChannels(image);
8311              }
8312
8313              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8314                 break;
8315            }
8316
8317            if (image->storage_class == PseudoClass && image->colormap != NULL)
8318            {
8319              for (i=0; i < (ssize_t) image->colors; i++)
8320              {
8321                LBR04PacketRGBO(image->colormap[i]);
8322              }
8323            }
8324          }
8325        else if (image->depth > 1)
8326          {
8327            /* Scale to 2-bit */
8328            LBR02PacketRGBO(image->background_color);
8329
8330            for (y=0; y < (ssize_t) image->rows; y++)
8331            {
8332              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8333
8334              if (r == (Quantum *) NULL)
8335                break;
8336
8337              for (x=0; x < (ssize_t) image->columns; x++)
8338              {
8339                 LBR02PixelRGBA(r);
8340                 r+=GetPixelChannels(image);
8341              }
8342
8343              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8344                 break;
8345            }
8346
8347            if (image->storage_class == PseudoClass && image->colormap != NULL)
8348            {
8349              for (i=0; i < (ssize_t) image->colors; i++)
8350              {
8351                LBR02PacketRGBO(image->colormap[i]);
8352              }
8353            }
8354          }
8355        else
8356          {
8357            /* Scale to 1-bit */
8358            LBR01PacketRGBO(image->background_color);
8359
8360            for (y=0; y < (ssize_t) image->rows; y++)
8361            {
8362              r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8363
8364              if (r == (Quantum *) NULL)
8365                break;
8366
8367              for (x=0; x < (ssize_t) image->columns; x++)
8368              {
8369                 LBR01PixelRGBA(r);
8370                 r+=GetPixelChannels(image);
8371              }
8372
8373              if (SyncAuthenticPixels(image,exception) == MagickFalse)
8374                 break;
8375            }
8376
8377            if (image->storage_class == PseudoClass && image->colormap != NULL)
8378            {
8379              for (i=0; i < (ssize_t) image->colors; i++)
8380              {
8381                LBR01PacketRGBO(image->colormap[i]);
8382              }
8383            }
8384          }
8385     }
8386
8387   /* To do: set to next higher multiple of 8 */
8388   if (image->depth < 8)
8389      image->depth=8;
8390
8391 #if (MAGICKCORE_QUANTUM_DEPTH > 16)
8392   /* PNG does not handle depths greater than 16 so reduce it even
8393    * if lossy
8394    */
8395   if (image->depth > 8)
8396       image->depth=16;
8397 #endif
8398
8399 #if (MAGICKCORE_QUANTUM_DEPTH > 8)
8400   if (image->depth > 8)
8401     {
8402       /* To do: fill low byte properly */
8403       image->depth=16;
8404     }
8405
8406   if (image->depth == 16 && mng_info->write_png_depth != 16)
8407     if (mng_info->write_png8 || LosslessReduceDepthOK(image,exception) != MagickFalse)
8408       image->depth = 8;
8409 #endif
8410
8411   image_colors = (int) image->colors;
8412   number_opaque = (int) image->colors;
8413   number_transparent = 0;
8414   number_semitransparent = 0;
8415
8416   if (mng_info->write_png_colortype &&
8417      (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
8418      mng_info->write_png_colortype < 4 &&
8419      image->alpha_trait == UndefinedPixelTrait)))
8420   {
8421      /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
8422       * are not going to need the result.
8423       */
8424      if (mng_info->write_png_colortype == 1 ||
8425         mng_info->write_png_colortype == 5)
8426        ping_have_color=MagickFalse;
8427
8428      if (image->alpha_trait != UndefinedPixelTrait)
8429        {
8430          number_transparent = 2;
8431          number_semitransparent = 1;
8432        }
8433   }
8434
8435   if (mng_info->write_png_colortype < 7)
8436   {
8437   /* BUILD_PALETTE
8438    *
8439    * Normally we run this just once, but in the case of writing PNG8
8440    * we reduce the transparency to binary and run again, then if there
8441    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
8442    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
8443    * palette.  Then (To do) we take care of a final reduction that is only
8444    * needed if there are still 256 colors present and one of them has both
8445    * transparent and opaque instances.
8446    */
8447
8448   tried_332 = MagickFalse;
8449   tried_333 = MagickFalse;
8450   tried_444 = MagickFalse;
8451
8452   for (j=0; j<6; j++)
8453   {
8454     /*
8455      * Sometimes we get DirectClass images that have 256 colors or fewer.
8456      * This code will build a colormap.
8457      *
8458      * Also, sometimes we get PseudoClass images with an out-of-date
8459      * colormap.  This code will replace the colormap with a new one.
8460      * Sometimes we get PseudoClass images that have more than 256 colors.
8461      * This code will delete the colormap and change the image to
8462      * DirectClass.
8463      *
8464      * If image->alpha_trait is MagickFalse, we ignore the alpha channel
8465      * even though it sometimes contains left-over non-opaque values.
8466      *
8467      * Also we gather some information (number of opaque, transparent,
8468      * and semitransparent pixels, and whether the image has any non-gray
8469      * pixels or only black-and-white pixels) that we might need later.
8470      *
8471      * Even if the user wants to force GrayAlpha or RGBA (colortype 4 or 6)
8472      * we need to check for bogus non-opaque values, at least.
8473      */
8474
8475    int
8476      n;
8477
8478    PixelInfo
8479      opaque[260],
8480      semitransparent[260],
8481      transparent[260];
8482
8483    register const Quantum
8484      *s;
8485
8486    register Quantum
8487      *q,
8488      *r;
8489
8490    if (logging != MagickFalse)
8491      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8492          "    Enter BUILD_PALETTE:");
8493
8494    if (logging != MagickFalse)
8495      {
8496        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8497              "      image->columns=%.20g",(double) image->columns);
8498        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8499              "      image->rows=%.20g",(double) image->rows);
8500        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8501              "      image->alpha_trait=%.20g",(double) image->alpha_trait);
8502        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8503              "      image->depth=%.20g",(double) image->depth);
8504
8505        if (image->storage_class == PseudoClass && image->colormap != NULL)
8506        {
8507          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8508              "      Original colormap:");
8509          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8510              "        i    (red,green,blue,alpha)");
8511
8512          for (i=0; i < 256; i++)
8513          {
8514                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8515                    "        %d    (%d,%d,%d,%d)",
8516                     (int) i,
8517                     (int) image->colormap[i].red,
8518                     (int) image->colormap[i].green,
8519                     (int) image->colormap[i].blue,
8520                     (int) image->colormap[i].alpha);
8521          }
8522
8523          for (i=image->colors - 10; i < (ssize_t) image->colors; i++)
8524          {
8525            if (i > 255)
8526              {
8527                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8528                    "        %d    (%d,%d,%d,%d)",
8529                     (int) i,
8530                     (int) image->colormap[i].red,
8531                     (int) image->colormap[i].green,
8532                     (int) image->colormap[i].blue,
8533                     (int) image->colormap[i].alpha);
8534              }
8535          }
8536        }
8537
8538        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8539            "      image->colors=%d",(int) image->colors);
8540
8541        if (image->colors == 0)
8542          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8543              "        (zero means unknown)");
8544
8545        if (ping_preserve_colormap == MagickFalse)
8546          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8547               "      Regenerate the colormap");
8548      }
8549
8550      image_colors=0;
8551      number_opaque = 0;
8552      number_semitransparent = 0;
8553      number_transparent = 0;
8554
8555      for (y=0; y < (ssize_t) image->rows; y++)
8556      {
8557        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8558
8559        if (q == (Quantum *) NULL)
8560          break;
8561
8562        for (x=0; x < (ssize_t) image->columns; x++)
8563        {
8564            if (image->alpha_trait == UndefinedPixelTrait ||
8565               GetPixelAlpha(image,q) == OpaqueAlpha)
8566              {
8567                if (number_opaque < 259)
8568                  {
8569                    if (number_opaque == 0)
8570                      {
8571                        GetPixelInfoPixel(image, q, opaque);
8572                        opaque[0].alpha=OpaqueAlpha;
8573                        number_opaque=1;
8574                      }
8575
8576                    for (i=0; i< (ssize_t) number_opaque; i++)
8577                      {
8578                        if (Magick_png_color_equal(image,q,opaque+i))
8579                          break;
8580                      }
8581
8582                    if (i ==  (ssize_t) number_opaque && number_opaque < 259)
8583                      {
8584                        number_opaque++;
8585                        GetPixelInfoPixel(image, q, opaque+i);
8586                        opaque[i].alpha=OpaqueAlpha;
8587                      }
8588                  }
8589              }
8590            else if (GetPixelAlpha(image,q) == TransparentAlpha)
8591              {
8592                if (number_transparent < 259)
8593                  {
8594                    if (number_transparent == 0)
8595                      {
8596                        GetPixelInfoPixel(image, q, transparent);
8597                        ping_trans_color.red=(unsigned short)
8598                          GetPixelRed(image,q);
8599                        ping_trans_color.green=(unsigned short)
8600                          GetPixelGreen(image,q);
8601                        ping_trans_color.blue=(unsigned short)
8602                          GetPixelBlue(image,q);
8603                        ping_trans_color.gray=(unsigned short)
8604                          GetPixelGray(image,q);
8605                        number_transparent = 1;
8606                      }
8607
8608                    for (i=0; i< (ssize_t) number_transparent; i++)
8609                      {
8610                        if (Magick_png_color_equal(image,q,transparent+i))
8611                          break;
8612                      }
8613
8614                    if (i ==  (ssize_t) number_transparent &&
8615                        number_transparent < 259)
8616                      {
8617                        number_transparent++;
8618                        GetPixelInfoPixel(image,q,transparent+i);
8619                      }
8620                  }
8621              }
8622            else
8623              {
8624                if (number_semitransparent < 259)
8625                  {
8626                    if (number_semitransparent == 0)
8627                      {
8628                        GetPixelInfoPixel(image,q,semitransparent);
8629                        number_semitransparent = 1;
8630                      }
8631
8632                    for (i=0; i< (ssize_t) number_semitransparent; i++)
8633                      {
8634                        if (Magick_png_color_equal(image,q,semitransparent+i)
8635                            && GetPixelAlpha(image,q) ==
8636                            semitransparent[i].alpha)
8637                          break;
8638                      }
8639
8640                    if (i ==  (ssize_t) number_semitransparent &&
8641                        number_semitransparent < 259)
8642                      {
8643                        number_semitransparent++;
8644                        GetPixelInfoPixel(image, q, semitransparent+i);
8645                      }
8646                  }
8647              }
8648            q+=GetPixelChannels(image);
8649         }
8650      }
8651
8652      if (mng_info->write_png8 == MagickFalse &&
8653          ping_exclude_bKGD == MagickFalse)
8654        {
8655          /* Add the background color to the palette, if it
8656           * isn't already there.
8657           */
8658           if (logging != MagickFalse)
8659             {
8660               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8661                   "      Check colormap for background (%d,%d,%d)",
8662                   (int) image->background_color.red,
8663                   (int) image->background_color.green,
8664                   (int) image->background_color.blue);
8665             }
8666           for (i=0; i<number_opaque; i++)
8667           {
8668              if (opaque[i].red == image->background_color.red &&
8669                  opaque[i].green == image->background_color.green &&
8670                  opaque[i].blue == image->background_color.blue)
8671                break;
8672           }
8673           if (number_opaque < 259 && i == number_opaque)
8674             {
8675                opaque[i] = image->background_color;
8676                ping_background.index = i;
8677                number_opaque++;
8678                if (logging != MagickFalse)
8679                  {
8680                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8681                        "      background_color index is %d",(int) i);
8682                  }
8683
8684             }
8685           else if (logging != MagickFalse)
8686               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8687                   "      No room in the colormap to add background color");
8688        }
8689
8690      image_colors=number_opaque+number_transparent+number_semitransparent;
8691
8692      if (logging != MagickFalse)
8693        {
8694          if (image_colors > 256)
8695             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8696                   "      image has more than 256 colors");
8697
8698          else
8699             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8700                   "      image has %d colors",image_colors);
8701        }
8702
8703      if (ping_preserve_colormap != MagickFalse)
8704        break;
8705
8706      if (mng_info->write_png_colortype != 7) /* We won't need this info */
8707        {
8708          ping_have_color=MagickFalse;
8709          ping_have_non_bw=MagickFalse;
8710
8711          if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
8712          {
8713            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8714               "incompatible colorspace");
8715            ping_have_color=MagickTrue;
8716            ping_have_non_bw=MagickTrue;
8717          }
8718
8719          if(image_colors > 256)
8720            {
8721              for (y=0; y < (ssize_t) image->rows; y++)
8722              {
8723                q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8724
8725                if (q == (Quantum *) NULL)
8726                  break;
8727
8728                s=q;
8729                for (x=0; x < (ssize_t) image->columns; x++)
8730                {
8731                  if (GetPixelRed(image,s) != GetPixelGreen(image,s) ||
8732                      GetPixelRed(image,s) != GetPixelBlue(image,s))
8733                    {
8734                       ping_have_color=MagickTrue;
8735                       ping_have_non_bw=MagickTrue;
8736                       break;
8737                    }
8738                  s+=GetPixelChannels(image);
8739                }
8740
8741                if (ping_have_color != MagickFalse)
8742                  break;
8743
8744                /* Worst case is black-and-white; we are looking at every
8745                 * pixel twice.
8746                 */
8747
8748                if (ping_have_non_bw == MagickFalse)
8749                  {
8750                    s=q;
8751                    for (x=0; x < (ssize_t) image->columns; x++)
8752                    {
8753                      if (GetPixelRed(image,s) != 0 &&
8754                          GetPixelRed(image,s) != QuantumRange)
8755                        {
8756                          ping_have_non_bw=MagickTrue;
8757                          break;
8758                        }
8759                      s+=GetPixelChannels(image);
8760                    }
8761                }
8762              }
8763            }
8764        }
8765
8766      if (image_colors < 257)
8767        {
8768          PixelInfo
8769            colormap[260];
8770
8771          /*
8772           * Initialize image colormap.
8773           */
8774
8775          if (logging != MagickFalse)
8776             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8777                   "      Sort the new colormap");
8778
8779         /* Sort palette, transparent first */;
8780
8781          n = 0;
8782
8783          for (i=0; i<number_transparent; i++)
8784             colormap[n++] = transparent[i];
8785
8786          for (i=0; i<number_semitransparent; i++)
8787             colormap[n++] = semitransparent[i];
8788
8789          for (i=0; i<number_opaque; i++)
8790             colormap[n++] = opaque[i];
8791
8792          ping_background.index +=
8793            (number_transparent + number_semitransparent);
8794
8795          /* image_colors < 257; search the colormap instead of the pixels
8796           * to get ping_have_color and ping_have_non_bw
8797           */
8798          for (i=0; i<n; i++)
8799          {
8800            if (ping_have_color == MagickFalse)
8801              {
8802                 if (colormap[i].red != colormap[i].green ||
8803                     colormap[i].red != colormap[i].blue)
8804                   {
8805                      ping_have_color=MagickTrue;
8806                      ping_have_non_bw=MagickTrue;
8807                      break;
8808                   }
8809               }
8810
8811            if (ping_have_non_bw == MagickFalse)
8812              {
8813                if (colormap[i].red != 0 && colormap[i].red != QuantumRange)
8814                    ping_have_non_bw=MagickTrue;
8815              }
8816           }
8817
8818         if ((mng_info->ping_exclude_tRNS == MagickFalse ||
8819             (number_transparent == 0 && number_semitransparent == 0)) &&
8820             (((mng_info->write_png_colortype-1) ==
8821             PNG_COLOR_TYPE_PALETTE) ||
8822             (mng_info->write_png_colortype == 0)))
8823           {
8824             if (logging != MagickFalse)
8825               {
8826                 if (n !=  (ssize_t) image_colors)
8827                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8828                    "   image_colors (%d) and n (%d)  don't match",
8829                    image_colors, n);
8830
8831                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8832                    "      AcquireImageColormap");
8833               }
8834
8835             image->colors = image_colors;
8836
8837             if (AcquireImageColormap(image,image_colors,exception) ==
8838                 MagickFalse)
8839                ThrowWriterException(ResourceLimitError,
8840                    "MemoryAllocationFailed");
8841
8842             for (i=0; i< (ssize_t) image_colors; i++)
8843                image->colormap[i] = colormap[i];
8844
8845             if (logging != MagickFalse)
8846               {
8847                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8848                       "      image->colors=%d (%d)",
8849                       (int) image->colors, image_colors);
8850
8851                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8852                       "      Update the pixel indexes");
8853               }
8854
8855             /* Sync the pixel indices with the new colormap */
8856
8857             for (y=0; y < (ssize_t) image->rows; y++)
8858             {
8859               q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8860
8861               if (q == (Quantum *) NULL)
8862                 break;
8863
8864               for (x=0; x < (ssize_t) image->columns; x++)
8865               {
8866                 for (i=0; i< (ssize_t) image_colors; i++)
8867                 {
8868                   if ((image->alpha_trait == UndefinedPixelTrait ||
8869                       image->colormap[i].alpha == GetPixelAlpha(image,q)) &&
8870                       image->colormap[i].red == GetPixelRed(image,q) &&
8871                       image->colormap[i].green == GetPixelGreen(image,q) &&
8872                       image->colormap[i].blue == GetPixelBlue(image,q))
8873                   {
8874                     SetPixelIndex(image,i,q);
8875                     break;
8876                   }
8877                 }
8878                 q+=GetPixelChannels(image);
8879               }
8880
8881               if (SyncAuthenticPixels(image,exception) == MagickFalse)
8882                  break;
8883             }
8884           }
8885        }
8886
8887      if (logging != MagickFalse)
8888        {
8889          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8890             "      image->colors=%d", (int) image->colors);
8891
8892          if (image->colormap != NULL)
8893            {
8894              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8895                  "       i     (red,green,blue,alpha)");
8896
8897              for (i=0; i < (ssize_t) image->colors; i++)
8898              {
8899                if (i < 300 || i >= (ssize_t) image->colors - 10)
8900                  {
8901                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8902                        "       %d     (%d,%d,%d,%d)",
8903                         (int) i,
8904                         (int) image->colormap[i].red,
8905                         (int) image->colormap[i].green,
8906                         (int) image->colormap[i].blue,
8907                         (int) image->colormap[i].alpha);
8908                  }
8909              }
8910            }
8911
8912            if (number_transparent < 257)
8913              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8914                    "      number_transparent     = %d",
8915                    number_transparent);
8916            else
8917
8918              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8919                    "      number_transparent     > 256");
8920
8921            if (number_opaque < 257)
8922              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8923                    "      number_opaque          = %d",
8924                    number_opaque);
8925
8926            else
8927              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8928                    "      number_opaque          > 256");
8929
8930            if (number_semitransparent < 257)
8931              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8932                    "      number_semitransparent = %d",
8933                    number_semitransparent);
8934
8935            else
8936              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8937                    "      number_semitransparent > 256");
8938
8939            if (ping_have_non_bw == MagickFalse)
8940               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8941                     "      All pixels and the background are black or white");
8942
8943            else if (ping_have_color == MagickFalse)
8944               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8945                     "      All pixels and the background are gray");
8946
8947            else
8948               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8949                     "      At least one pixel or the background is non-gray");
8950
8951            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8952                "    Exit BUILD_PALETTE:");
8953        }
8954
8955    if (mng_info->write_png8 == MagickFalse)
8956       break;
8957
8958    /* Make any reductions necessary for the PNG8 format */
8959     if (image_colors <= 256 &&
8960         image_colors != 0 && image->colormap != NULL &&
8961         number_semitransparent == 0 &&
8962         number_transparent <= 1)
8963       break;
8964
8965     /* PNG8 can't have semitransparent colors so we threshold the
8966      * opacity to 0 or OpaqueOpacity, and PNG8 can only have one
8967      * transparent color so if more than one is transparent we merge
8968      * them into image->background_color.
8969      */
8970     if (number_semitransparent != 0 || number_transparent > 1)
8971       {
8972         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
8973             "    Thresholding the alpha channel to binary");
8974
8975         for (y=0; y < (ssize_t) image->rows; y++)
8976         {
8977           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
8978
8979           if (r == (Quantum *) NULL)
8980             break;
8981
8982           for (x=0; x < (ssize_t) image->columns; x++)
8983           {
8984               if (GetPixelAlpha(image,r) < OpaqueAlpha/2)
8985                 {
8986                   SetPixelViaPixelInfo(image,&image->background_color,r);
8987                   SetPixelAlpha(image,TransparentAlpha,r);
8988                 }
8989               else
8990                   SetPixelAlpha(image,OpaqueAlpha,r);
8991               r+=GetPixelChannels(image);
8992           }
8993
8994           if (SyncAuthenticPixels(image,exception) == MagickFalse)
8995              break;
8996
8997           if (image_colors != 0 && image_colors <= 256 &&
8998              image->colormap != NULL)
8999             for (i=0; i<image_colors; i++)
9000                 image->colormap[i].alpha =
9001                     (image->colormap[i].alpha > TransparentAlpha/2 ?
9002                     TransparentAlpha : OpaqueAlpha);
9003         }
9004       continue;
9005     }
9006
9007     /* PNG8 can't have more than 256 colors so we quantize the pixels and
9008      * background color to the 4-4-4-1, 3-3-3-1 or 3-3-2-1 palette.  If the
9009      * image is mostly gray, the 4-4-4-1 palette is likely to end up with 256
9010      * colors or less.
9011      */
9012     if (tried_444 == MagickFalse && (image_colors == 0 || image_colors > 256))
9013       {
9014         if (logging != MagickFalse)
9015            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9016                "    Quantizing the background color to 4-4-4");
9017
9018         tried_444 = MagickTrue;
9019
9020         LBR04PacketRGB(image->background_color);
9021
9022         if (logging != MagickFalse)
9023           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9024               "    Quantizing the pixel colors to 4-4-4");
9025
9026         if (image->colormap == NULL)
9027         {
9028           for (y=0; y < (ssize_t) image->rows; y++)
9029           {
9030             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9031
9032             if (r == (Quantum *) NULL)
9033               break;
9034
9035             for (x=0; x < (ssize_t) image->columns; x++)
9036             {
9037               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9038                   LBR04PixelRGB(r);
9039               r+=GetPixelChannels(image);
9040             }
9041
9042             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9043                break;
9044           }
9045         }
9046
9047         else /* Should not reach this; colormap already exists and
9048                 must be <= 256 */
9049         {
9050           if (logging != MagickFalse)
9051               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9052               "    Quantizing the colormap to 4-4-4");
9053
9054           for (i=0; i<image_colors; i++)
9055           {
9056             LBR04PacketRGB(image->colormap[i]);
9057           }
9058         }
9059         continue;
9060       }
9061
9062     if (tried_333 == MagickFalse && (image_colors == 0 || image_colors > 256))
9063       {
9064         if (logging != MagickFalse)
9065            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9066                "    Quantizing the background color to 3-3-3");
9067
9068         tried_333 = MagickTrue;
9069
9070         LBR03PacketRGB(image->background_color);
9071
9072         if (logging != MagickFalse)
9073           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9074               "    Quantizing the pixel colors to 3-3-3-1");
9075
9076         if (image->colormap == NULL)
9077         {
9078           for (y=0; y < (ssize_t) image->rows; y++)
9079           {
9080             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9081
9082             if (r == (Quantum *) NULL)
9083               break;
9084
9085             for (x=0; x < (ssize_t) image->columns; x++)
9086             {
9087               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9088                   LBR03RGB(r);
9089               r+=GetPixelChannels(image);
9090             }
9091
9092             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9093                break;
9094           }
9095         }
9096
9097         else /* Should not reach this; colormap already exists and
9098                 must be <= 256 */
9099         {
9100           if (logging != MagickFalse)
9101               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9102               "    Quantizing the colormap to 3-3-3-1");
9103           for (i=0; i<image_colors; i++)
9104           {
9105               LBR03PacketRGB(image->colormap[i]);
9106           }
9107         }
9108         continue;
9109       }
9110
9111     if (tried_332 == MagickFalse && (image_colors == 0 || image_colors > 256))
9112       {
9113         if (logging != MagickFalse)
9114            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9115                "    Quantizing the background color to 3-3-2");
9116
9117         tried_332 = MagickTrue;
9118
9119         /* Red and green were already done so we only quantize the blue
9120          * channel
9121          */
9122
9123         LBR02PacketBlue(image->background_color);
9124
9125         if (logging != MagickFalse)
9126           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9127               "    Quantizing the pixel colors to 3-3-2-1");
9128
9129         if (image->colormap == NULL)
9130         {
9131           for (y=0; y < (ssize_t) image->rows; y++)
9132           {
9133             r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9134
9135             if (r == (Quantum *) NULL)
9136               break;
9137
9138             for (x=0; x < (ssize_t) image->columns; x++)
9139             {
9140               if (GetPixelAlpha(image,r) == OpaqueAlpha)
9141                   LBR02PixelBlue(r);
9142               r+=GetPixelChannels(image);
9143             }
9144
9145             if (SyncAuthenticPixels(image,exception) == MagickFalse)
9146                break;
9147           }
9148         }
9149
9150         else /* Should not reach this; colormap already exists and
9151                 must be <= 256 */
9152         {
9153           if (logging != MagickFalse)
9154               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9155               "    Quantizing the colormap to 3-3-2-1");
9156           for (i=0; i<image_colors; i++)
9157           {
9158               LBR02PacketBlue(image->colormap[i]);
9159           }
9160       }
9161       continue;
9162     }
9163
9164     if (image_colors == 0 || image_colors > 256)
9165     {
9166       /* Take care of special case with 256 opaque colors + 1 transparent
9167        * color.  We don't need to quantize to 2-3-2-1; we only need to
9168        * eliminate one color, so we'll merge the two darkest red
9169        * colors (0x49, 0, 0) -> (0x24, 0, 0).
9170        */
9171       if (logging != MagickFalse)
9172         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9173             "    Merging two dark red background colors to 3-3-2-1");
9174
9175       if (ScaleQuantumToChar(image->background_color.red) == 0x49 &&
9176           ScaleQuantumToChar(image->background_color.green) == 0x00 &&
9177           ScaleQuantumToChar(image->background_color.blue) == 0x00)
9178       {
9179          image->background_color.red=ScaleCharToQuantum(0x24);
9180       }
9181
9182       if (logging != MagickFalse)
9183         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9184             "    Merging two dark red pixel colors to 3-3-2-1");
9185
9186       if (image->colormap == NULL)
9187       {
9188         for (y=0; y < (ssize_t) image->rows; y++)
9189         {
9190           r=GetAuthenticPixels(image,0,y,image->columns,1,exception);
9191
9192           if (r == (Quantum *) NULL)
9193             break;
9194
9195           for (x=0; x < (ssize_t) image->columns; x++)
9196           {
9197             if (ScaleQuantumToChar(GetPixelRed(image,r)) == 0x49 &&
9198                 ScaleQuantumToChar(GetPixelGreen(image,r)) == 0x00 &&
9199                 ScaleQuantumToChar(GetPixelBlue(image,r)) == 0x00 &&
9200                 GetPixelAlpha(image,r) == OpaqueAlpha)
9201               {
9202                 SetPixelRed(image,ScaleCharToQuantum(0x24),r);
9203               }
9204             r+=GetPixelChannels(image);
9205           }
9206
9207           if (SyncAuthenticPixels(image,exception) == MagickFalse)
9208              break;
9209
9210         }
9211       }
9212
9213       else
9214       {
9215          for (i=0; i<image_colors; i++)
9216          {
9217             if (ScaleQuantumToChar(image->colormap[i].red) == 0x49 &&
9218                 ScaleQuantumToChar(image->colormap[i].green) == 0x00 &&
9219                 ScaleQuantumToChar(image->colormap[i].blue) == 0x00)
9220             {
9221                image->colormap[i].red=ScaleCharToQuantum(0x24);
9222             }
9223          }
9224       }
9225     }
9226   }
9227   }
9228   /* END OF BUILD_PALETTE */
9229
9230   /* If we are excluding the tRNS chunk and there is transparency,
9231    * then we must write a Gray-Alpha (color-type 4) or RGBA (color-type 6)
9232    * PNG.
9233    */
9234   if (mng_info->ping_exclude_tRNS != MagickFalse &&
9235      (number_transparent != 0 || number_semitransparent != 0))
9236     {
9237       unsigned int colortype=mng_info->write_png_colortype;
9238
9239       if (ping_have_color == MagickFalse)
9240         mng_info->write_png_colortype = 5;
9241
9242       else
9243         mng_info->write_png_colortype = 7;
9244
9245       if (colortype != 0 &&
9246          mng_info->write_png_colortype != colortype)
9247         ping_need_colortype_warning=MagickTrue;
9248
9249     }
9250
9251   /* See if cheap transparency is possible.  It is only possible
9252    * when there is a single transparent color, no semitransparent
9253    * color, and no opaque color that has the same RGB components
9254    * as the transparent color.  We only need this information if
9255    * we are writing a PNG with colortype 0 or 2, and we have not
9256    * excluded the tRNS chunk.
9257    */
9258   if (number_transparent == 1 &&
9259       mng_info->write_png_colortype < 4)
9260     {
9261        ping_have_cheap_transparency = MagickTrue;
9262
9263        if (number_semitransparent != 0)
9264          ping_have_cheap_transparency = MagickFalse;
9265
9266        else if (image_colors == 0 || image_colors > 256 ||
9267            image->colormap == NULL)
9268          {
9269            register const Quantum
9270              *q;
9271
9272            for (y=0; y < (ssize_t) image->rows; y++)
9273            {
9274              q=GetVirtualPixels(image,0,y,image->columns,1, exception);
9275
9276              if (q == (Quantum *) NULL)
9277                break;
9278
9279              for (x=0; x < (ssize_t) image->columns; x++)
9280              {
9281                  if (GetPixelAlpha(image,q) != TransparentAlpha &&
9282                      (unsigned short) GetPixelRed(image,q) ==
9283                                      ping_trans_color.red &&
9284                      (unsigned short) GetPixelGreen(image,q) ==
9285                                      ping_trans_color.green &&
9286                      (unsigned short) GetPixelBlue(image,q) ==
9287                                      ping_trans_color.blue)
9288                    {
9289                      ping_have_cheap_transparency = MagickFalse;
9290                      break;
9291                    }
9292
9293                  q+=GetPixelChannels(image);
9294              }
9295
9296              if (ping_have_cheap_transparency == MagickFalse)
9297                 break;
9298            }
9299          }
9300        else
9301          {
9302             /* Assuming that image->colormap[0] is the one transparent color
9303              * and that all others are opaque.
9304              */
9305             if (image_colors > 1)
9306               for (i=1; i<image_colors; i++)
9307                 if (image->colormap[i].red == image->colormap[0].red &&
9308                     image->colormap[i].green == image->colormap[0].green &&
9309                     image->colormap[i].blue == image->colormap[0].blue)
9310                   {
9311                      ping_have_cheap_transparency = MagickFalse;
9312                      break;
9313                   }
9314          }
9315
9316        if (logging != MagickFalse)
9317          {
9318            if (ping_have_cheap_transparency == MagickFalse)
9319              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9320                  "   Cheap transparency is not possible.");
9321
9322            else
9323              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9324                  "   Cheap transparency is possible.");
9325          }
9326      }
9327   else
9328     ping_have_cheap_transparency = MagickFalse;
9329
9330   image_depth=image->depth;
9331
9332   quantum_info = (QuantumInfo *) NULL;
9333   number_colors=0;
9334   image_colors=(int) image->colors;
9335   image_matte=image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse;
9336
9337   if (mng_info->write_png_colortype < 5)
9338     mng_info->IsPalette=image->storage_class == PseudoClass &&
9339       image_colors <= 256 && image->colormap != NULL;
9340   else
9341     mng_info->IsPalette = MagickFalse;
9342
9343   if ((mng_info->write_png_colortype == 4 || mng_info->write_png8) &&
9344      (image->colors == 0 || image->colormap == NULL))
9345     {
9346       image_info=DestroyImageInfo(image_info);
9347       image=DestroyImage(image);
9348       (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
9349           "Cannot write PNG8 or color-type 3; colormap is NULL",
9350           "`%s'",IMimage->filename);
9351       return(MagickFalse);
9352     }
9353
9354   /*
9355     Allocate the PNG structures
9356   */
9357 #ifdef PNG_USER_MEM_SUPPORTED
9358  error_info.image=image;
9359  error_info.exception=exception;
9360   ping=png_create_write_struct_2(PNG_LIBPNG_VER_STRING,&error_info,
9361     MagickPNGErrorHandler,MagickPNGWarningHandler,(void *) NULL,
9362     (png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
9363
9364 #else
9365   ping=png_create_write_struct(PNG_LIBPNG_VER_STRING,&error_info,
9366     MagickPNGErrorHandler,MagickPNGWarningHandler);
9367
9368 #endif
9369   if (ping == (png_struct *) NULL)
9370     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9371
9372   ping_info=png_create_info_struct(ping);
9373
9374   if (ping_info == (png_info *) NULL)
9375     {
9376       png_destroy_write_struct(&ping,(png_info **) NULL);
9377       ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
9378     }
9379
9380   png_set_write_fn(ping,image,png_put_data,png_flush_data);
9381   pixel_info=(MemoryInfo *) NULL;
9382
9383   if (setjmp(png_jmpbuf(ping)))
9384     {
9385       /*
9386         PNG write failed.
9387       */
9388 #ifdef PNG_DEBUG
9389      if (image_info->verbose)
9390         (void) printf("PNG write has failed.\n");
9391 #endif
9392       png_destroy_write_struct(&ping,&ping_info);
9393 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9394       UnlockSemaphoreInfo(ping_semaphore);
9395 #endif
9396
9397       if (pixel_info != (MemoryInfo *) NULL)
9398         pixel_info=RelinquishVirtualMemory(pixel_info);
9399
9400       if (quantum_info != (QuantumInfo *) NULL)
9401         quantum_info=DestroyQuantumInfo(quantum_info);
9402
9403       if (ping_have_blob != MagickFalse)
9404           (void) CloseBlob(image);
9405       image_info=DestroyImageInfo(image_info);
9406       image=DestroyImage(image);
9407       return(MagickFalse);
9408     }
9409
9410   /* {  For navigation to end of SETJMP-protected block.  Within this
9411    *    block, use png_error() instead of Throwing an Exception, to ensure
9412    *    that libpng is able to clean up, and that the semaphore is unlocked.
9413    */
9414
9415 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
9416   LockSemaphoreInfo(ping_semaphore);
9417 #endif
9418
9419 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
9420   /* Allow benign errors */
9421   png_set_benign_errors(ping, 1);
9422 #endif
9423
9424 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
9425   /* Reject images with too many rows or columns */
9426   png_set_user_limits(ping,
9427     (png_uint_32) MagickMin(0x7fffffffL,
9428         GetMagickResourceLimit(WidthResource)),
9429     (png_uint_32) MagickMin(0x7fffffffL,
9430         GetMagickResourceLimit(HeightResource)));
9431 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
9432
9433   /*
9434     Prepare PNG for writing.
9435   */
9436
9437 #if defined(PNG_MNG_FEATURES_SUPPORTED)
9438   if (mng_info->write_mng)
9439   {
9440      (void) png_permit_mng_features(ping,PNG_ALL_MNG_FEATURES);
9441 # ifdef PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
9442      /* Disable new libpng-1.5.10 feature when writing a MNG because
9443       * zero-length PLTE is OK
9444       */
9445      png_set_check_for_invalid_index (ping, 0);
9446 # endif
9447   }
9448
9449 #else
9450 # ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
9451   if (mng_info->write_mng)
9452      png_permit_empty_plte(ping,MagickTrue);
9453
9454 # endif
9455 #endif
9456
9457   x=0;
9458
9459   ping_width=(png_uint_32) image->columns;
9460   ping_height=(png_uint_32) image->rows;
9461
9462   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
9463      image_depth=8;
9464
9465   if (mng_info->write_png48 || mng_info->write_png64)
9466      image_depth=16;
9467
9468   if (mng_info->write_png_depth != 0)
9469      image_depth=mng_info->write_png_depth;
9470
9471   /* Adjust requested depth to next higher valid depth if necessary */
9472   if (image_depth > 8)
9473      image_depth=16;
9474
9475   if ((image_depth > 4) && (image_depth < 8))
9476      image_depth=8;
9477
9478   if (image_depth == 3)
9479      image_depth=4;
9480
9481   if (logging != MagickFalse)
9482     {
9483      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9484         "    width=%.20g",(double) ping_width);
9485      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9486         "    height=%.20g",(double) ping_height);
9487      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9488         "    image_matte=%.20g",(double) image->alpha_trait);
9489      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9490         "    image->depth=%.20g",(double) image->depth);
9491      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9492         "    Tentative ping_bit_depth=%.20g",(double) image_depth);
9493     }
9494
9495   save_image_depth=image_depth;
9496   ping_bit_depth=(png_byte) save_image_depth;
9497
9498
9499 #if defined(PNG_pHYs_SUPPORTED)
9500   if (ping_exclude_pHYs == MagickFalse)
9501   {
9502   if ((image->resolution.x != 0) && (image->resolution.y != 0) &&
9503       (!mng_info->write_mng || !mng_info->equal_physs))
9504     {
9505       if (logging != MagickFalse)
9506         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9507             "    Setting up pHYs chunk");
9508
9509       if (image->units == PixelsPerInchResolution)
9510         {
9511           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9512           ping_pHYs_x_resolution=
9513              (png_uint_32) ((100.0*image->resolution.x+0.5)/2.54);
9514           ping_pHYs_y_resolution=
9515              (png_uint_32) ((100.0*image->resolution.y+0.5)/2.54);
9516         }
9517
9518       else if (image->units == PixelsPerCentimeterResolution)
9519         {
9520           ping_pHYs_unit_type=PNG_RESOLUTION_METER;
9521           ping_pHYs_x_resolution=(png_uint_32) (100.0*image->resolution.x+0.5);
9522           ping_pHYs_y_resolution=(png_uint_32) (100.0*image->resolution.y+0.5);
9523         }
9524
9525       else
9526         {
9527           ping_pHYs_unit_type=PNG_RESOLUTION_UNKNOWN;
9528           ping_pHYs_x_resolution=(png_uint_32) image->resolution.x;
9529           ping_pHYs_y_resolution=(png_uint_32) image->resolution.y;
9530         }
9531
9532       if (logging != MagickFalse)
9533         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9534           "    Set up PNG pHYs chunk: xres: %.20g, yres: %.20g, units: %d.",
9535           (double) ping_pHYs_x_resolution,(double) ping_pHYs_y_resolution,
9536           (int) ping_pHYs_unit_type);
9537        ping_have_pHYs = MagickTrue;
9538     }
9539   }
9540 #endif
9541
9542   if (ping_exclude_bKGD == MagickFalse)
9543   {
9544   if ((!mng_info->adjoin || !mng_info->equal_backgrounds))
9545     {
9546        unsigned int
9547          mask;
9548
9549        mask=0xffff;
9550        if (ping_bit_depth == 8)
9551           mask=0x00ff;
9552
9553        if (ping_bit_depth == 4)
9554           mask=0x000f;
9555
9556        if (ping_bit_depth == 2)
9557           mask=0x0003;
9558
9559        if (ping_bit_depth == 1)
9560           mask=0x0001;
9561
9562        ping_background.red=(png_uint_16)
9563          (ScaleQuantumToShort(image->background_color.red) & mask);
9564
9565        ping_background.green=(png_uint_16)
9566          (ScaleQuantumToShort(image->background_color.green) & mask);
9567
9568        ping_background.blue=(png_uint_16)
9569          (ScaleQuantumToShort(image->background_color.blue) & mask);
9570
9571        ping_background.gray=(png_uint_16) ping_background.green;
9572     }
9573
9574   if (logging != MagickFalse)
9575     {
9576       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9577           "    Setting up bKGD chunk (1)");
9578       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9579           "      background_color index is %d",
9580           (int) ping_background.index);
9581
9582       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9583           "    ping_bit_depth=%d",ping_bit_depth);
9584     }
9585
9586   ping_have_bKGD = MagickTrue;
9587   }
9588
9589   /*
9590     Select the color type.
9591   */
9592   matte=image_matte;
9593   old_bit_depth=0;
9594
9595   if (mng_info->IsPalette && mng_info->write_png8)
9596     {
9597       /* To do: make this a function cause it's used twice, except
9598          for reducing the sample depth from 8. */
9599
9600       number_colors=image_colors;
9601
9602       ping_have_tRNS=MagickFalse;
9603
9604       /*
9605         Set image palette.
9606       */
9607       ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9608
9609       if (logging != MagickFalse)
9610         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9611             "  Setting up PLTE chunk with %d colors (%d)",
9612             number_colors, image_colors);
9613
9614       for (i=0; i < (ssize_t) number_colors; i++)
9615       {
9616         palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
9617         palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
9618         palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
9619         if (logging != MagickFalse)
9620           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9621 #if MAGICKCORE_QUANTUM_DEPTH == 8
9622             "    %3ld (%3d,%3d,%3d)",
9623 #else
9624             "    %5ld (%5d,%5d,%5d)",
9625 #endif
9626             (long) i,palette[i].red,palette[i].green,palette[i].blue);
9627
9628       }
9629
9630       ping_have_PLTE=MagickTrue;
9631       image_depth=ping_bit_depth;
9632       ping_num_trans=0;
9633
9634       if (matte != MagickFalse)
9635       {
9636           /*
9637             Identify which colormap entry is transparent.
9638           */
9639           assert(number_colors <= 256);
9640           assert(image->colormap != NULL);
9641
9642           for (i=0; i < (ssize_t) number_transparent; i++)
9643              ping_trans_alpha[i]=0;
9644
9645
9646           ping_num_trans=(unsigned short) (number_transparent +
9647              number_semitransparent);
9648
9649           if (ping_num_trans == 0)
9650              ping_have_tRNS=MagickFalse;
9651
9652           else
9653              ping_have_tRNS=MagickTrue;
9654       }
9655
9656       if (ping_exclude_bKGD == MagickFalse)
9657       {
9658        /*
9659         * Identify which colormap entry is the background color.
9660         */
9661
9662         for (i=0; i < (ssize_t) MagickMax(1L*number_colors-1L,1L); i++)
9663           if (IsPNGColorEqual(ping_background,image->colormap[i]))
9664             break;
9665
9666         ping_background.index=(png_byte) i;
9667
9668         if (logging != MagickFalse)
9669           {
9670             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9671                  "      background_color index is %d",
9672                  (int) ping_background.index);
9673           }
9674       }
9675     } /* end of write_png8 */
9676
9677   else if (mng_info->write_png_colortype == 1)
9678     {
9679       image_matte=MagickFalse;
9680       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9681     }
9682
9683   else if (mng_info->write_png24 || mng_info->write_png48 ||
9684       mng_info->write_png_colortype == 3)
9685     {
9686       image_matte=MagickFalse;
9687       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9688     }
9689
9690   else if (mng_info->write_png32 || mng_info->write_png64 ||
9691       mng_info->write_png_colortype == 7)
9692     {
9693       image_matte=MagickTrue;
9694       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9695     }
9696
9697   else /* mng_info->write_pngNN not specified */
9698     {
9699       image_depth=ping_bit_depth;
9700
9701       if (mng_info->write_png_colortype != 0)
9702         {
9703           ping_color_type=(png_byte) mng_info->write_png_colortype-1;
9704
9705           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9706               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9707             image_matte=MagickTrue;
9708
9709           else
9710             image_matte=MagickFalse;
9711
9712           if (logging != MagickFalse)
9713              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9714              "   PNG colortype %d was specified:",(int) ping_color_type);
9715         }
9716
9717       else /* write_png_colortype not specified */
9718         {
9719           if (logging != MagickFalse)
9720              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9721              "  Selecting PNG colortype:");
9722
9723           ping_color_type=(png_byte) ((matte != MagickFalse)?
9724             PNG_COLOR_TYPE_RGB_ALPHA:PNG_COLOR_TYPE_RGB);
9725
9726           if (image_info->type == TrueColorType)
9727             {
9728               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9729               image_matte=MagickFalse;
9730             }
9731
9732           if (image_info->type == TrueColorAlphaType)
9733             {
9734               ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
9735               image_matte=MagickTrue;
9736             }
9737
9738           if (image_info->type == PaletteType ||
9739               image_info->type == PaletteAlphaType)
9740             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
9741
9742           if (mng_info->write_png_colortype == 0 &&
9743              image_info->type == UndefinedType)
9744             {
9745               if (ping_have_color == MagickFalse)
9746                 {
9747                   if (image_matte == MagickFalse)
9748                     {
9749                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
9750                       image_matte=MagickFalse;
9751                     }
9752
9753                   else
9754                     {
9755                       ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY_ALPHA;
9756                       image_matte=MagickTrue;
9757                     }
9758                 }
9759               else
9760                 {
9761                   if (image_matte == MagickFalse)
9762                     {
9763                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
9764                       image_matte=MagickFalse;
9765                     }
9766
9767                   else
9768                     {
9769                       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGBA;
9770                       image_matte=MagickTrue;
9771                     }
9772                  }
9773             }
9774
9775         }
9776
9777       if (logging != MagickFalse)
9778          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9779          "    Selected PNG colortype=%d",ping_color_type);
9780
9781       if (ping_bit_depth < 8)
9782         {
9783           if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
9784               ping_color_type == PNG_COLOR_TYPE_RGB ||
9785               ping_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
9786             ping_bit_depth=8;
9787         }
9788
9789       old_bit_depth=ping_bit_depth;
9790
9791       if (ping_color_type == PNG_COLOR_TYPE_GRAY)
9792         {
9793           if (image->alpha_trait == UndefinedPixelTrait && ping_have_non_bw == MagickFalse)
9794              ping_bit_depth=1;
9795         }
9796
9797       if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
9798         {
9799            size_t one = 1;
9800            ping_bit_depth=1;
9801
9802            if (image->colors == 0)
9803            {
9804               /* DO SOMETHING */
9805                 png_error(ping,"image has 0 colors");
9806            }
9807
9808            while ((int) (one << ping_bit_depth) < (ssize_t) image_colors)
9809              ping_bit_depth <<= 1;
9810         }
9811
9812       if (logging != MagickFalse)
9813          {
9814            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9815             "    Number of colors: %.20g",(double) image_colors);
9816
9817            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9818             "    Tentative PNG bit depth: %d",ping_bit_depth);
9819          }
9820
9821       if (ping_bit_depth < (int) mng_info->write_png_depth)
9822          ping_bit_depth = mng_info->write_png_depth;
9823     }
9824
9825   image_depth=ping_bit_depth;
9826
9827   if (logging != MagickFalse)
9828     {
9829       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9830         "    Tentative PNG color type: %s (%.20g)",
9831         PngColorTypeToString(ping_color_type),
9832         (double) ping_color_type);
9833
9834       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9835         "    image_info->type: %.20g",(double) image_info->type);
9836
9837       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9838         "    image_depth: %.20g",(double) image_depth);
9839
9840       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9841
9842         "    image->depth: %.20g",(double) image->depth);
9843
9844       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9845         "    ping_bit_depth: %.20g",(double) ping_bit_depth);
9846     }
9847
9848   if (matte != MagickFalse)
9849     {
9850       if (mng_info->IsPalette)
9851         {
9852           if (mng_info->write_png_colortype == 0)
9853             {
9854               ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9855
9856               if (ping_have_color != MagickFalse)
9857                  ping_color_type=PNG_COLOR_TYPE_RGBA;
9858             }
9859
9860           /*
9861            * Determine if there is any transparent color.
9862           */
9863           if (number_transparent + number_semitransparent == 0)
9864             {
9865               /*
9866                 No transparent pixels are present.  Change 4 or 6 to 0 or 2.
9867               */
9868
9869               image_matte=MagickFalse;
9870
9871               if (mng_info->write_png_colortype == 0)
9872                 ping_color_type&=0x03;
9873             }
9874
9875           else
9876             {
9877               unsigned int
9878                 mask;
9879
9880               mask=0xffff;
9881
9882               if (ping_bit_depth == 8)
9883                  mask=0x00ff;
9884
9885               if (ping_bit_depth == 4)
9886                  mask=0x000f;
9887
9888               if (ping_bit_depth == 2)
9889                  mask=0x0003;
9890
9891               if (ping_bit_depth == 1)
9892                  mask=0x0001;
9893
9894               ping_trans_color.red=(png_uint_16)
9895                 (ScaleQuantumToShort(image->colormap[0].red) & mask);
9896
9897               ping_trans_color.green=(png_uint_16)
9898                 (ScaleQuantumToShort(image->colormap[0].green) & mask);
9899
9900               ping_trans_color.blue=(png_uint_16)
9901                 (ScaleQuantumToShort(image->colormap[0].blue) & mask);
9902
9903               ping_trans_color.gray=(png_uint_16)
9904                 (ScaleQuantumToShort(GetPixelInfoIntensity(image,
9905                    image->colormap)) & mask);
9906
9907               ping_trans_color.index=(png_byte) 0;
9908
9909               ping_have_tRNS=MagickTrue;
9910             }
9911
9912           if (ping_have_tRNS != MagickFalse)
9913             {
9914               /*
9915                * Determine if there is one and only one transparent color
9916                * and if so if it is fully transparent.
9917                */
9918               if (ping_have_cheap_transparency == MagickFalse)
9919                 ping_have_tRNS=MagickFalse;
9920             }
9921
9922           if (ping_have_tRNS != MagickFalse)
9923             {
9924               if (mng_info->write_png_colortype == 0)
9925                 ping_color_type &= 0x03;  /* changes 4 or 6 to 0 or 2 */
9926
9927               if (image_depth == 8)
9928                 {
9929                   ping_trans_color.red&=0xff;
9930                   ping_trans_color.green&=0xff;
9931                   ping_trans_color.blue&=0xff;
9932                   ping_trans_color.gray&=0xff;
9933                 }
9934             }
9935         }
9936       else
9937         {
9938           if (image_depth == 8)
9939             {
9940               ping_trans_color.red&=0xff;
9941               ping_trans_color.green&=0xff;
9942               ping_trans_color.blue&=0xff;
9943               ping_trans_color.gray&=0xff;
9944             }
9945         }
9946     }
9947
9948     matte=image_matte;
9949
9950     if (ping_have_tRNS != MagickFalse)
9951       image_matte=MagickFalse;
9952
9953     if ((mng_info->IsPalette) &&
9954         mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE &&
9955         ping_have_color == MagickFalse &&
9956         (image_matte == MagickFalse || image_depth >= 8))
9957       {
9958         size_t one=1;
9959
9960         if (image_matte != MagickFalse)
9961           ping_color_type=PNG_COLOR_TYPE_GRAY_ALPHA;
9962
9963         else if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_GRAY_ALPHA)
9964           {
9965             ping_color_type=PNG_COLOR_TYPE_GRAY;
9966
9967             if (save_image_depth == 16 && image_depth == 8)
9968               {
9969                 if (logging != MagickFalse)
9970                   {
9971                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
9972                         "  Scaling ping_trans_color (0)");
9973                   }
9974                     ping_trans_color.gray*=0x0101;
9975               }
9976           }
9977
9978         if (image_depth > MAGICKCORE_QUANTUM_DEPTH)
9979           image_depth=MAGICKCORE_QUANTUM_DEPTH;
9980
9981         if ((image_colors == 0) ||
9982              ((ssize_t) (image_colors-1) > (ssize_t) MaxColormapSize))
9983           image_colors=(int) (one << image_depth);
9984
9985         if (image_depth > 8)
9986           ping_bit_depth=16;
9987
9988         else
9989           {
9990             ping_bit_depth=8;
9991             if ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
9992               {
9993                 if(!mng_info->write_png_depth)
9994                   {
9995                     ping_bit_depth=1;
9996
9997                     while ((int) (one << ping_bit_depth)
9998                         < (ssize_t) image_colors)
9999                       ping_bit_depth <<= 1;
10000                   }
10001               }
10002
10003             else if (ping_color_type ==
10004                 PNG_COLOR_TYPE_GRAY && image_colors < 17 &&
10005                 mng_info->IsPalette)
10006               {
10007               /* Check if grayscale is reducible */
10008
10009                 int
10010                   depth_4_ok=MagickTrue,
10011                   depth_2_ok=MagickTrue,
10012                   depth_1_ok=MagickTrue;
10013
10014                 for (i=0; i < (ssize_t) image_colors; i++)
10015                 {
10016                    unsigned char
10017                      intensity;
10018
10019                    intensity=ScaleQuantumToChar(image->colormap[i].red);
10020
10021                    if ((intensity & 0x0f) != ((intensity & 0xf0) >> 4))
10022                      depth_4_ok=depth_2_ok=depth_1_ok=MagickFalse;
10023                    else if ((intensity & 0x03) != ((intensity & 0x0c) >> 2))
10024                      depth_2_ok=depth_1_ok=MagickFalse;
10025                    else if ((intensity & 0x01) != ((intensity & 0x02) >> 1))
10026                      depth_1_ok=MagickFalse;
10027                 }
10028
10029                 if (depth_1_ok && mng_info->write_png_depth <= 1)
10030                   ping_bit_depth=1;
10031
10032                 else if (depth_2_ok && mng_info->write_png_depth <= 2)
10033                   ping_bit_depth=2;
10034
10035                 else if (depth_4_ok && mng_info->write_png_depth <= 4)
10036                   ping_bit_depth=4;
10037               }
10038           }
10039
10040           image_depth=ping_bit_depth;
10041       }
10042
10043     else
10044
10045       if (mng_info->IsPalette)
10046       {
10047         number_colors=image_colors;
10048
10049         if (image_depth <= 8)
10050           {
10051             /*
10052               Set image palette.
10053             */
10054             ping_color_type=(png_byte) PNG_COLOR_TYPE_PALETTE;
10055
10056             if (!(mng_info->have_write_global_plte && matte == MagickFalse))
10057               {
10058                 for (i=0; i < (ssize_t) number_colors; i++)
10059                 {
10060                   palette[i].red=ScaleQuantumToChar(image->colormap[i].red);
10061                   palette[i].green=ScaleQuantumToChar(image->colormap[i].green);
10062                   palette[i].blue=ScaleQuantumToChar(image->colormap[i].blue);
10063                 }
10064
10065                 if (logging != MagickFalse)
10066                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10067                     "  Setting up PLTE chunk with %d colors",
10068                     number_colors);
10069
10070                 ping_have_PLTE=MagickTrue;
10071               }
10072
10073             /* color_type is PNG_COLOR_TYPE_PALETTE */
10074             if (mng_info->write_png_depth == 0)
10075               {
10076                 size_t
10077                   one;
10078
10079                 ping_bit_depth=1;
10080                 one=1;
10081
10082                 while ((one << ping_bit_depth) < (size_t) number_colors)
10083                   ping_bit_depth <<= 1;
10084               }
10085
10086             ping_num_trans=0;
10087
10088             if (matte != MagickFalse)
10089               {
10090                 /*
10091                  * Set up trans_colors array.
10092                  */
10093                 assert(number_colors <= 256);
10094
10095                 ping_num_trans=(unsigned short) (number_transparent +
10096                   number_semitransparent);
10097
10098                 if (ping_num_trans == 0)
10099                   ping_have_tRNS=MagickFalse;
10100
10101                 else
10102                   {
10103                     if (logging != MagickFalse)
10104                       {
10105                         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10106                           "  Scaling ping_trans_color (1)");
10107                       }
10108                     ping_have_tRNS=MagickTrue;
10109
10110                     for (i=0; i < ping_num_trans; i++)
10111                     {
10112                        ping_trans_alpha[i]= (png_byte)
10113                          ScaleQuantumToChar(image->colormap[i].alpha);
10114                     }
10115                   }
10116               }
10117           }
10118       }
10119
10120     else
10121       {
10122
10123         if (image_depth < 8)
10124           image_depth=8;
10125
10126         if ((save_image_depth == 16) && (image_depth == 8))
10127           {
10128             if (logging != MagickFalse)
10129               {
10130                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10131                   "    Scaling ping_trans_color from (%d,%d,%d)",
10132                   (int) ping_trans_color.red,
10133                   (int) ping_trans_color.green,
10134                   (int) ping_trans_color.blue);
10135               }
10136
10137             ping_trans_color.red*=0x0101;
10138             ping_trans_color.green*=0x0101;
10139             ping_trans_color.blue*=0x0101;
10140             ping_trans_color.gray*=0x0101;
10141
10142             if (logging != MagickFalse)
10143               {
10144                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10145                   "    to (%d,%d,%d)",
10146                   (int) ping_trans_color.red,
10147                   (int) ping_trans_color.green,
10148                   (int) ping_trans_color.blue);
10149               }
10150           }
10151       }
10152
10153     if (ping_bit_depth <  (ssize_t) mng_info->write_png_depth)
10154          ping_bit_depth =  (ssize_t) mng_info->write_png_depth;
10155
10156     /*
10157       Adjust background and transparency samples in sub-8-bit grayscale files.
10158     */
10159     if (ping_bit_depth < 8 && ping_color_type ==
10160         PNG_COLOR_TYPE_GRAY)
10161       {
10162          png_uint_16
10163            maxval;
10164
10165          size_t
10166            one=1;
10167
10168          maxval=(png_uint_16) ((one << ping_bit_depth)-1);
10169
10170          if (ping_exclude_bKGD == MagickFalse)
10171          {
10172
10173          ping_background.gray=(png_uint_16) ((maxval/65535.)*
10174            (ScaleQuantumToShort(((GetPixelInfoIntensity(image,
10175            &image->background_color))) +.5)));
10176
10177          if (logging != MagickFalse)
10178            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10179              "  Setting up bKGD chunk (2)");
10180          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10181              "      background_color index is %d",
10182              (int) ping_background.index);
10183
10184          ping_have_bKGD = MagickTrue;
10185          }
10186
10187          if (logging != MagickFalse)
10188            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10189              "  Scaling ping_trans_color.gray from %d",
10190              (int)ping_trans_color.gray);
10191
10192          ping_trans_color.gray=(png_uint_16) ((maxval/255.)*(
10193            ping_trans_color.gray)+.5);
10194
10195          if (logging != MagickFalse)
10196            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10197              "      to %d", (int)ping_trans_color.gray);
10198       }
10199
10200   if (ping_exclude_bKGD == MagickFalse)
10201   {
10202     if (mng_info->IsPalette && (int) ping_color_type == PNG_COLOR_TYPE_PALETTE)
10203       {
10204         /*
10205            Identify which colormap entry is the background color.
10206         */
10207
10208         number_colors=image_colors;
10209
10210         for (i=0; i < (ssize_t) MagickMax(1L*number_colors,1L); i++)
10211           if (IsPNGColorEqual(image->background_color,image->colormap[i]))
10212             break;
10213
10214         ping_background.index=(png_byte) i;
10215
10216         if (logging != MagickFalse)
10217           {
10218             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10219               "  Setting up bKGD chunk with index=%d",(int) i);
10220           }
10221
10222         if (i < (ssize_t) number_colors)
10223           {
10224             ping_have_bKGD = MagickTrue;
10225
10226             if (logging != MagickFalse)
10227               {
10228                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10229                   "     background   =(%d,%d,%d)",
10230                         (int) ping_background.red,
10231                         (int) ping_background.green,
10232                         (int) ping_background.blue);
10233               }
10234           }
10235
10236         else  /* Can't happen */
10237           {
10238             if (logging != MagickFalse)
10239               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10240                   "      No room in PLTE to add bKGD color");
10241             ping_have_bKGD = MagickFalse;
10242           }
10243       }
10244   }
10245
10246   if (logging != MagickFalse)
10247     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10248       "    PNG color type: %s (%d)", PngColorTypeToString(ping_color_type),
10249       ping_color_type);
10250   /*
10251     Initialize compression level and filtering.
10252   */
10253   if (logging != MagickFalse)
10254     {
10255       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10256         "  Setting up deflate compression");
10257
10258       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10259         "    Compression buffer size: 32768");
10260     }
10261
10262   png_set_compression_buffer_size(ping,32768L);
10263
10264   if (logging != MagickFalse)
10265     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10266       "    Compression mem level: 9");
10267
10268   png_set_compression_mem_level(ping, 9);
10269
10270   /* Untangle the "-quality" setting:
10271
10272      Undefined is 0; the default is used.
10273      Default is 75
10274
10275      10's digit:
10276
10277         0 or omitted: Use Z_HUFFMAN_ONLY strategy with the
10278            zlib default compression level
10279
10280         1-9: the zlib compression level
10281
10282      1's digit:
10283
10284         0-4: the PNG filter method
10285
10286         5:   libpng adaptive filtering if compression level > 5
10287              libpng filter type "none" if compression level <= 5
10288                 or if image is grayscale or palette
10289
10290         6:   libpng adaptive filtering
10291
10292         7:   "LOCO" filtering (intrapixel differing) if writing
10293              a MNG, otherwise "none".  Did not work in IM-6.7.0-9
10294              and earlier because of a missing "else".
10295
10296         8:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), adaptive
10297              filtering. Unused prior to IM-6.7.0-10, was same as 6
10298
10299         9:   Z_RLE strategy (or Z_HUFFMAN_ONLY if quality < 10), no PNG filters
10300              Unused prior to IM-6.7.0-10, was same as 6
10301
10302     Note that using the -quality option, not all combinations of
10303     PNG filter type, zlib compression level, and zlib compression
10304     strategy are possible.  This will be addressed soon in a
10305     release that accomodates "-define png:compression-strategy", etc.
10306
10307    */
10308
10309   quality=image_info->quality == UndefinedCompressionQuality ? 75UL :
10310      image_info->quality;
10311
10312   if (quality <= 9)
10313     {
10314       if (mng_info->write_png_compression_strategy == 0)
10315         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
10316     }
10317
10318   else if (mng_info->write_png_compression_level == 0)
10319     {
10320       int
10321         level;
10322
10323       level=(int) MagickMin((ssize_t) quality/10,9);
10324
10325       mng_info->write_png_compression_level = level+1;
10326     }
10327
10328   if (mng_info->write_png_compression_strategy == 0)
10329     {
10330         if ((quality %10) == 8 || (quality %10) == 9)
10331 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
10332           mng_info->write_png_compression_strategy=Z_RLE+1;
10333 #else
10334           mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
10335 #endif
10336     }
10337
10338   if (mng_info->write_png_compression_filter == 0)
10339         mng_info->write_png_compression_filter=((int) quality % 10) + 1;
10340
10341   if (logging != MagickFalse)
10342     {
10343         if (mng_info->write_png_compression_level)
10344           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10345             "    Compression level:    %d",
10346             (int) mng_info->write_png_compression_level-1);
10347
10348         if (mng_info->write_png_compression_strategy)
10349           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10350             "    Compression strategy: %d",
10351             (int) mng_info->write_png_compression_strategy-1);
10352
10353         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10354           "  Setting up filtering");
10355
10356         if (mng_info->write_png_compression_filter == 6)
10357           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10358             "    Base filter method: ADAPTIVE");
10359         else if (mng_info->write_png_compression_filter == 0 ||
10360                  mng_info->write_png_compression_filter == 1)
10361           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10362             "    Base filter method: NONE");
10363         else
10364           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10365             "    Base filter method: %d",
10366             (int) mng_info->write_png_compression_filter-1);
10367     }
10368
10369   if (mng_info->write_png_compression_level != 0)
10370     png_set_compression_level(ping,mng_info->write_png_compression_level-1);
10371
10372   if (mng_info->write_png_compression_filter == 6)
10373     {
10374       if (((int) ping_color_type == PNG_COLOR_TYPE_GRAY) ||
10375          ((int) ping_color_type == PNG_COLOR_TYPE_PALETTE) ||
10376          (quality < 50))
10377         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10378       else
10379         png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10380      }
10381   else if (mng_info->write_png_compression_filter == 7 ||
10382       mng_info->write_png_compression_filter == 10)
10383     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_ALL_FILTERS);
10384
10385   else if (mng_info->write_png_compression_filter == 8)
10386     {
10387 #if defined(PNG_MNG_FEATURES_SUPPORTED) && defined(PNG_INTRAPIXEL_DIFFERENCING)
10388       if (mng_info->write_mng)
10389       {
10390          if (((int) ping_color_type == PNG_COLOR_TYPE_RGB) ||
10391              ((int) ping_color_type == PNG_COLOR_TYPE_RGBA))
10392         ping_filter_method=PNG_INTRAPIXEL_DIFFERENCING;
10393       }
10394 #endif
10395       png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10396     }
10397
10398   else if (mng_info->write_png_compression_filter == 9)
10399     png_set_filter(ping,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
10400
10401   else if (mng_info->write_png_compression_filter != 0)
10402     png_set_filter(ping,PNG_FILTER_TYPE_BASE,
10403        mng_info->write_png_compression_filter-1);
10404
10405   if (mng_info->write_png_compression_strategy != 0)
10406     png_set_compression_strategy(ping,
10407        mng_info->write_png_compression_strategy-1);
10408
10409   ping_interlace_method=image_info->interlace != NoInterlace;
10410
10411   if (mng_info->write_mng)
10412     png_set_sig_bytes(ping,8);
10413
10414   /* Bail out if cannot meet defined png:bit-depth or png:color-type */
10415
10416   if (mng_info->write_png_colortype != 0)
10417     {
10418      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY)
10419        if (ping_have_color != MagickFalse)
10420          {
10421            ping_color_type = PNG_COLOR_TYPE_RGB;
10422
10423            if (ping_bit_depth < 8)
10424              ping_bit_depth=8;
10425          }
10426
10427      if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_GRAY_ALPHA)
10428        if (ping_have_color != MagickFalse)
10429          ping_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
10430     }
10431
10432   if (ping_need_colortype_warning != MagickFalse ||
10433      ((mng_info->write_png_depth &&
10434      (int) mng_info->write_png_depth != ping_bit_depth) ||
10435      (mng_info->write_png_colortype &&
10436      ((int) mng_info->write_png_colortype-1 != ping_color_type &&
10437       mng_info->write_png_colortype != 7 &&
10438       !(mng_info->write_png_colortype == 5 && ping_color_type == 0)))))
10439     {
10440       if (logging != MagickFalse)
10441         {
10442           if (ping_need_colortype_warning != MagickFalse)
10443             {
10444               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10445                  "  Image has transparency but tRNS chunk was excluded");
10446             }
10447
10448           if (mng_info->write_png_depth)
10449             {
10450               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10451                   "  Defined png:bit-depth=%u, Computed depth=%u",
10452                   mng_info->write_png_depth,
10453                   ping_bit_depth);
10454             }
10455
10456           if (mng_info->write_png_colortype)
10457             {
10458               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10459                   "  Defined png:color-type=%u, Computed color type=%u",
10460                   mng_info->write_png_colortype-1,
10461                   ping_color_type);
10462             }
10463         }
10464
10465       png_warning(ping,
10466         "Cannot write image with defined png:bit-depth or png:color-type.");
10467     }
10468
10469   if (image_matte != MagickFalse && image->alpha_trait == UndefinedPixelTrait)
10470     {
10471       /* Add an opaque matte channel */
10472       image->alpha_trait = BlendPixelTrait;
10473       (void) SetImageAlpha(image,OpaqueAlpha,exception);
10474
10475       if (logging != MagickFalse)
10476         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10477           "  Added an opaque matte channel");
10478     }
10479
10480   if (number_transparent != 0 || number_semitransparent != 0)
10481     {
10482       if (ping_color_type < 4)
10483         {
10484            ping_have_tRNS=MagickTrue;
10485            if (logging != MagickFalse)
10486              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10487                "  Setting ping_have_tRNS=MagickTrue.");
10488         }
10489     }
10490
10491   if (logging != MagickFalse)
10492     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10493       "  Writing PNG header chunks");
10494
10495   png_set_IHDR(ping,ping_info,ping_width,ping_height,
10496                ping_bit_depth,ping_color_type,
10497                ping_interlace_method,ping_compression_method,
10498                ping_filter_method);
10499
10500   if (ping_color_type == 3 && ping_have_PLTE != MagickFalse)
10501     {
10502       png_set_PLTE(ping,ping_info,palette,number_colors);
10503
10504       if (logging != MagickFalse)
10505         {
10506           for (i=0; i< (ssize_t) number_colors; i++)
10507           {
10508             if (i < ping_num_trans)
10509               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10510                 "     PLTE[%d] = (%d,%d,%d), tRNS[%d] = (%d)",
10511                       (int) i,
10512                       (int) palette[i].red,
10513                       (int) palette[i].green,
10514                       (int) palette[i].blue,
10515                       (int) i,
10516                       (int) ping_trans_alpha[i]);
10517              else
10518               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10519                 "     PLTE[%d] = (%d,%d,%d)",
10520                       (int) i,
10521                       (int) palette[i].red,
10522                       (int) palette[i].green,
10523                       (int) palette[i].blue);
10524            }
10525          }
10526     }
10527
10528   /* Only write the iCCP chunk if we are not writing the sRGB chunk. */
10529   if (ping_exclude_sRGB != MagickFalse ||
10530      (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10531   {
10532     if ((ping_exclude_tEXt == MagickFalse ||
10533        ping_exclude_zTXt == MagickFalse) &&
10534        (ping_exclude_iCCP == MagickFalse || ping_exclude_zCCP == MagickFalse))
10535     {
10536       ResetImageProfileIterator(image);
10537       for (name=GetNextImageProfile(image); name != (const char *) NULL; )
10538       {
10539         profile=GetImageProfile(image,name);
10540
10541         if (profile != (StringInfo *) NULL)
10542           {
10543 #ifdef PNG_WRITE_iCCP_SUPPORTED
10544             if ((LocaleCompare(name,"ICC") == 0) ||
10545                 (LocaleCompare(name,"ICM") == 0))
10546              {
10547
10548                if (ping_exclude_iCCP == MagickFalse)
10549                  {
10550                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10551                           "  Setting up iCCP chunk");
10552
10553                        png_set_iCCP(ping,ping_info,(png_charp) name,0,
10554 #if (PNG_LIBPNG_VER < 10500)
10555                          (png_charp) GetStringInfoDatum(profile),
10556 #else
10557                          (const png_byte *) GetStringInfoDatum(profile),
10558 #endif
10559                          (png_uint_32) GetStringInfoLength(profile));
10560                        ping_have_iCCP = MagickTrue;
10561                  }
10562              }
10563
10564             else
10565 #endif
10566               if (ping_exclude_zCCP == MagickFalse)
10567                 {
10568                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10569                       "  Setting up zTXT chunk with uuencoded ICC");
10570                   Magick_png_write_raw_profile(image_info,ping,ping_info,
10571                     (unsigned char *) name,(unsigned char *) name,
10572                     GetStringInfoDatum(profile),
10573                     (png_uint_32) GetStringInfoLength(profile));
10574                   ping_have_iCCP = MagickTrue;
10575                 }
10576           }
10577
10578           if (logging != MagickFalse)
10579             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10580               "  Setting up text chunk with %s profile",name);
10581
10582         name=GetNextImageProfile(image);
10583       }
10584     }
10585   }
10586
10587 #if defined(PNG_WRITE_sRGB_SUPPORTED)
10588   if ((mng_info->have_write_global_srgb == 0) &&
10589       ping_have_iCCP != MagickTrue &&
10590       (ping_have_sRGB != MagickFalse ||
10591       png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10592     {
10593       if (ping_exclude_sRGB == MagickFalse)
10594         {
10595           /*
10596             Note image rendering intent.
10597           */
10598           if (logging != MagickFalse)
10599             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10600                 "  Setting up sRGB chunk");
10601
10602           (void) png_set_sRGB(ping,ping_info,(
10603             Magick_RenderingIntent_to_PNG_RenderingIntent(
10604               image->rendering_intent)));
10605
10606           ping_have_sRGB = MagickTrue;
10607         }
10608     }
10609
10610   if ((!mng_info->write_mng) || (!png_get_valid(ping,ping_info,PNG_INFO_sRGB)))
10611 #endif
10612     {
10613       if (ping_exclude_gAMA == MagickFalse &&
10614           ping_have_iCCP == MagickFalse &&
10615           ping_have_sRGB == MagickFalse &&
10616           (ping_exclude_sRGB == MagickFalse ||
10617           (image->gamma < .45 || image->gamma > .46)))
10618       {
10619       if ((mng_info->have_write_global_gama == 0) && (image->gamma != 0.0))
10620         {
10621           /*
10622             Note image gamma.
10623             To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
10624           */
10625           if (logging != MagickFalse)
10626             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10627               "  Setting up gAMA chunk");
10628
10629           png_set_gAMA(ping,ping_info,image->gamma);
10630         }
10631       }
10632
10633       if (ping_exclude_cHRM == MagickFalse && ping_have_sRGB == MagickFalse)
10634         {
10635           if ((mng_info->have_write_global_chrm == 0) &&
10636               (image->chromaticity.red_primary.x != 0.0))
10637             {
10638               /*
10639                 Note image chromaticity.
10640                 Note: if cHRM+gAMA == sRGB write sRGB instead.
10641               */
10642                PrimaryInfo
10643                  bp,
10644                  gp,
10645                  rp,
10646                  wp;
10647
10648                wp=image->chromaticity.white_point;
10649                rp=image->chromaticity.red_primary;
10650                gp=image->chromaticity.green_primary;
10651                bp=image->chromaticity.blue_primary;
10652
10653                if (logging != MagickFalse)
10654                  (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10655                    "  Setting up cHRM chunk");
10656
10657                png_set_cHRM(ping,ping_info,wp.x,wp.y,rp.x,rp.y,gp.x,gp.y,
10658                    bp.x,bp.y);
10659            }
10660         }
10661     }
10662
10663   if (ping_exclude_bKGD == MagickFalse)
10664     {
10665       if (ping_have_bKGD != MagickFalse)
10666         {
10667           png_set_bKGD(ping,ping_info,&ping_background);
10668           if (logging != MagickFalse)
10669             {
10670               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10671                    "    Setting up bKGD chunk");
10672               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10673                    "      background color = (%d,%d,%d)",
10674                         (int) ping_background.red,
10675                         (int) ping_background.green,
10676                         (int) ping_background.blue);
10677               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10678                    "      index = %d, gray=%d",
10679                         (int) ping_background.index,
10680                         (int) ping_background.gray);
10681             }
10682          }
10683     }
10684
10685   if (ping_exclude_pHYs == MagickFalse)
10686     {
10687       if (ping_have_pHYs != MagickFalse)
10688         {
10689           png_set_pHYs(ping,ping_info,
10690              ping_pHYs_x_resolution,
10691              ping_pHYs_y_resolution,
10692              ping_pHYs_unit_type);
10693
10694           if (logging != MagickFalse)
10695             {
10696               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10697                    "    Setting up pHYs chunk");
10698               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10699                    "      x_resolution=%lu",
10700                    (unsigned long) ping_pHYs_x_resolution);
10701               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10702                    "      y_resolution=%lu",
10703                    (unsigned long) ping_pHYs_y_resolution);
10704               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10705                    "      unit_type=%lu",
10706                    (unsigned long) ping_pHYs_unit_type);
10707             }
10708         }
10709     }
10710
10711 #if defined(PNG_oFFs_SUPPORTED)
10712   if (ping_exclude_oFFs == MagickFalse)
10713     {
10714       if (image->page.x || image->page.y)
10715         {
10716            png_set_oFFs(ping,ping_info,(png_int_32) image->page.x,
10717               (png_int_32) image->page.y, 0);
10718
10719            if (logging != MagickFalse)
10720              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10721                  "    Setting up oFFs chunk with x=%d, y=%d, units=0",
10722                  (int) image->page.x, (int) image->page.y);
10723         }
10724     }
10725 #endif
10726
10727 #if defined(PNG_tIME_SUPPORTED)
10728   if (ping_exclude_tIME == MagickFalse)
10729     {
10730       const char
10731         *timestamp;
10732
10733       if (image->taint == MagickFalse)
10734         {
10735           timestamp=GetImageOption(image_info,"png:tIME");
10736
10737           if (timestamp == (const char *) NULL)
10738             timestamp=GetImageProperty(image,"png:tIME",exception);
10739         }
10740
10741       else
10742         {
10743           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10744              "  Reset tIME in tainted image");
10745
10746           timestamp=GetImageProperty(image,"date:modify",exception);
10747         }
10748
10749       if (timestamp != (const char *) NULL)
10750           write_tIME_chunk(image,ping,ping_info,timestamp,exception);
10751     }
10752 #endif
10753
10754   if (mng_info->need_blob != MagickFalse)
10755   {
10756     if (OpenBlob(image_info,image,WriteBinaryBlobMode,exception) ==
10757        MagickFalse)
10758        png_error(ping,"WriteBlob Failed");
10759
10760      ping_have_blob=MagickTrue;
10761   }
10762
10763   png_write_info_before_PLTE(ping, ping_info);
10764
10765   if (ping_have_tRNS != MagickFalse && ping_color_type < 4)
10766     {
10767       if (logging != MagickFalse)
10768         {
10769           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10770               "  Calling png_set_tRNS with num_trans=%d",ping_num_trans);
10771         }
10772
10773       if (ping_color_type == 3)
10774          (void) png_set_tRNS(ping, ping_info,
10775                 ping_trans_alpha,
10776                 ping_num_trans,
10777                 NULL);
10778
10779       else
10780         {
10781            (void) png_set_tRNS(ping, ping_info,
10782                   NULL,
10783                   0,
10784                   &ping_trans_color);
10785
10786            if (logging != MagickFalse)
10787              {
10788                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10789                  "     tRNS color   =(%d,%d,%d)",
10790                        (int) ping_trans_color.red,
10791                        (int) ping_trans_color.green,
10792                        (int) ping_trans_color.blue);
10793              }
10794          }
10795     }
10796
10797   /* write any png-chunk-b profiles */
10798   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-b",logging);
10799
10800   png_write_info(ping,ping_info);
10801
10802   /* write any PNG-chunk-m profiles */
10803   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-m",logging);
10804
10805   if (ping_exclude_vpAg == MagickFalse)
10806     {
10807       if ((image->page.width != 0 && image->page.width != image->columns) ||
10808           (image->page.height != 0 && image->page.height != image->rows))
10809         {
10810           unsigned char
10811             chunk[14];
10812
10813           (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
10814           PNGType(chunk,mng_vpAg);
10815           LogPNGChunk(logging,mng_vpAg,9L);
10816           PNGLong(chunk+4,(png_uint_32) image->page.width);
10817           PNGLong(chunk+8,(png_uint_32) image->page.height);
10818           chunk[12]=0;   /* unit = pixels */
10819           (void) WriteBlob(image,13,chunk);
10820           (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
10821         }
10822     }
10823
10824 #if (PNG_LIBPNG_VER == 10206)
10825     /* avoid libpng-1.2.6 bug by setting PNG_HAVE_IDAT flag */
10826 #define PNG_HAVE_IDAT               0x04
10827     ping->mode |= PNG_HAVE_IDAT;
10828 #undef PNG_HAVE_IDAT
10829 #endif
10830
10831   png_set_packing(ping);
10832   /*
10833     Allocate memory.
10834   */
10835   rowbytes=image->columns;
10836   if (image_depth > 8)
10837     rowbytes*=2;
10838   switch (ping_color_type)
10839     {
10840       case PNG_COLOR_TYPE_RGB:
10841         rowbytes*=3;
10842         break;
10843
10844       case PNG_COLOR_TYPE_GRAY_ALPHA:
10845         rowbytes*=2;
10846         break;
10847
10848       case PNG_COLOR_TYPE_RGBA:
10849         rowbytes*=4;
10850         break;
10851
10852       default:
10853         break;
10854     }
10855
10856   if (logging != MagickFalse)
10857     {
10858       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10859         "  Writing PNG image data");
10860
10861       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10862         "    Allocating %.20g bytes of memory for pixels",(double) rowbytes);
10863     }
10864   pixel_info=AcquireVirtualMemory(rowbytes,sizeof(*ping_pixels));
10865   if (pixel_info == (MemoryInfo *) NULL)
10866     png_error(ping,"Allocation of memory for pixels failed");
10867   ping_pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
10868
10869   /*
10870     Initialize image scanlines.
10871   */
10872   quantum_info=AcquireQuantumInfo(image_info,image);
10873   if (quantum_info == (QuantumInfo *) NULL)
10874     png_error(ping,"Memory allocation for quantum_info failed");
10875   quantum_info->format=UndefinedQuantumFormat;
10876   SetQuantumDepth(image,quantum_info,image_depth);
10877   (void) SetQuantumEndian(image,quantum_info,MSBEndian);
10878   num_passes=png_set_interlace_handling(ping);
10879
10880   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10881        !mng_info->write_png48 && !mng_info->write_png64 &&
10882        !mng_info->write_png32) &&
10883        (mng_info->IsPalette ||
10884        (image_info->type == BilevelType)) &&
10885        image_matte == MagickFalse &&
10886        ping_have_non_bw == MagickFalse)
10887     {
10888       /* Palette, Bilevel, or Opaque Monochrome */
10889       register const Quantum
10890         *p;
10891
10892       SetQuantumDepth(image,quantum_info,8);
10893       for (pass=0; pass < num_passes; pass++)
10894       {
10895         /*
10896           Convert PseudoClass image to a PNG monochrome image.
10897         */
10898         for (y=0; y < (ssize_t) image->rows; y++)
10899         {
10900           if (logging != MagickFalse && y == 0)
10901              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10902                  "    Writing row of pixels (0)");
10903
10904           p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10905
10906           if (p == (const Quantum *) NULL)
10907             break;
10908
10909           if (mng_info->IsPalette)
10910             {
10911               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10912                 quantum_info,GrayQuantum,ping_pixels,exception);
10913               if (mng_info->write_png_colortype-1 == PNG_COLOR_TYPE_PALETTE &&
10914                   mng_info->write_png_depth &&
10915                   mng_info->write_png_depth != old_bit_depth)
10916                 {
10917                   /* Undo pixel scaling */
10918                   for (i=0; i < (ssize_t) image->columns; i++)
10919                      *(ping_pixels+i)=(unsigned char) (*(ping_pixels+i)
10920                      >> (8-old_bit_depth));
10921                 }
10922             }
10923
10924           else
10925             {
10926               (void) ExportQuantumPixels(image,(CacheView *) NULL,
10927                 quantum_info,RedQuantum,ping_pixels,exception);
10928             }
10929
10930           if (mng_info->write_png_colortype-1 != PNG_COLOR_TYPE_PALETTE)
10931             for (i=0; i < (ssize_t) image->columns; i++)
10932                *(ping_pixels+i)=(unsigned char) ((*(ping_pixels+i) > 127) ?
10933                       255 : 0);
10934
10935           if (logging != MagickFalse && y == 0)
10936             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10937                 "    Writing row of pixels (1)");
10938
10939           png_write_row(ping,ping_pixels);
10940
10941           status=SetImageProgress(image,SaveImageTag,
10942               (MagickOffsetType) (pass * image->rows + y),
10943               num_passes * image->rows);
10944
10945           if (status == MagickFalse)
10946             break;
10947         }
10948       }
10949     }
10950
10951   else   /* Not Palette, Bilevel, or Opaque Monochrome */
10952     {
10953       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
10954           !mng_info->write_png48 && !mng_info->write_png64 &&
10955           !mng_info->write_png32) && (image_matte != MagickFalse ||
10956           (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
10957           (mng_info->IsPalette) && ping_have_color == MagickFalse)
10958         {
10959           register const Quantum
10960             *p;
10961
10962           for (pass=0; pass < num_passes; pass++)
10963           {
10964
10965           for (y=0; y < (ssize_t) image->rows; y++)
10966           {
10967             p=GetVirtualPixels(image,0,y,image->columns,1,exception);
10968
10969             if (p == (const Quantum *) NULL)
10970               break;
10971
10972             if (ping_color_type == PNG_COLOR_TYPE_GRAY)
10973               {
10974                 if (mng_info->IsPalette)
10975                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10976                     quantum_info,GrayQuantum,ping_pixels,exception);
10977
10978                 else
10979                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
10980                     quantum_info,RedQuantum,ping_pixels,exception);
10981
10982                 if (logging != MagickFalse && y == 0)
10983                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10984                        "    Writing GRAY PNG pixels (2)");
10985               }
10986
10987             else /* PNG_COLOR_TYPE_GRAY_ALPHA */
10988               {
10989                 if (logging != MagickFalse && y == 0)
10990                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10991                          "    Writing GRAY_ALPHA PNG pixels (2)");
10992
10993                 (void) ExportQuantumPixels(image,(CacheView *) NULL,
10994                   quantum_info,GrayAlphaQuantum,ping_pixels,exception);
10995               }
10996
10997             if (logging != MagickFalse && y == 0)
10998               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
10999                   "    Writing row of pixels (2)");
11000
11001             png_write_row(ping,ping_pixels);
11002
11003             status=SetImageProgress(image,SaveImageTag,
11004               (MagickOffsetType) (pass * image->rows + y),
11005               num_passes * image->rows);
11006
11007             if (status == MagickFalse)
11008               break;
11009             }
11010           }
11011         }
11012
11013       else
11014         {
11015           register const Quantum
11016             *p;
11017
11018           for (pass=0; pass < num_passes; pass++)
11019           {
11020             if ((image_depth > 8) ||
11021                 mng_info->write_png24 ||
11022                 mng_info->write_png32 ||
11023                 mng_info->write_png48 ||
11024                 mng_info->write_png64 ||
11025                 (!mng_info->write_png8 && !mng_info->IsPalette))
11026             {
11027               for (y=0; y < (ssize_t) image->rows; y++)
11028               {
11029                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11030
11031                 if (p == (const Quantum *) NULL)
11032                   break;
11033
11034                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11035                   {
11036                     if (image->storage_class == DirectClass)
11037                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11038                         quantum_info,RedQuantum,ping_pixels,exception);
11039
11040                     else
11041                       (void) ExportQuantumPixels(image,(CacheView *) NULL,
11042                         quantum_info,GrayQuantum,ping_pixels,exception);
11043                   }
11044
11045                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11046                   {
11047                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11048                       quantum_info,GrayAlphaQuantum,ping_pixels,
11049                       exception);
11050
11051                     if (logging != MagickFalse && y == 0)
11052                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11053                            "    Writing GRAY_ALPHA PNG pixels (3)");
11054                   }
11055
11056                 else if (image_matte != MagickFalse)
11057                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11058                     quantum_info,RGBAQuantum,ping_pixels,exception);
11059
11060                 else
11061                   (void) ExportQuantumPixels(image,(CacheView *) NULL,
11062                     quantum_info,RGBQuantum,ping_pixels,exception);
11063
11064                 if (logging != MagickFalse && y == 0)
11065                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11066                       "    Writing row of pixels (3)");
11067
11068                 png_write_row(ping,ping_pixels);
11069
11070                 status=SetImageProgress(image,SaveImageTag,
11071                   (MagickOffsetType) (pass * image->rows + y),
11072                   num_passes * image->rows);
11073
11074                 if (status == MagickFalse)
11075                   break;
11076               }
11077             }
11078
11079           else
11080             /* not ((image_depth > 8) ||
11081                 mng_info->write_png24 || mng_info->write_png32 ||
11082                 mng_info->write_png48 || mng_info->write_png64 ||
11083                 (!mng_info->write_png8 && !mng_info->IsPalette))
11084              */
11085             {
11086               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
11087                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
11088                 {
11089                   if (logging != MagickFalse)
11090                     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11091                       "  pass %d, Image Is not GRAY or GRAY_ALPHA",pass);
11092
11093                   SetQuantumDepth(image,quantum_info,8);
11094                   image_depth=8;
11095                 }
11096
11097               for (y=0; y < (ssize_t) image->rows; y++)
11098               {
11099                 if (logging != MagickFalse && y == 0)
11100                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11101                     "  pass %d, Image Is RGB, 16-bit GRAY, or GRAY_ALPHA",pass);
11102
11103                 p=GetVirtualPixels(image,0,y,image->columns,1, exception);
11104
11105                 if (p == (const Quantum *) NULL)
11106                   break;
11107
11108                 if (ping_color_type == PNG_COLOR_TYPE_GRAY)
11109                   {
11110                     SetQuantumDepth(image,quantum_info,image->depth);
11111
11112                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11113                        quantum_info,GrayQuantum,ping_pixels,exception);
11114                   }
11115
11116                 else if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
11117                   {
11118                     if (logging != MagickFalse && y == 0)
11119                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11120                            "  Writing GRAY_ALPHA PNG pixels (4)");
11121
11122                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11123                          quantum_info,GrayAlphaQuantum,ping_pixels,
11124                          exception);
11125                   }
11126
11127                 else
11128                   {
11129                     (void) ExportQuantumPixels(image,(CacheView *) NULL,
11130                       quantum_info,IndexQuantum,ping_pixels,exception);
11131
11132                     if (logging != MagickFalse && y <= 2)
11133                     {
11134                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11135                           "  Writing row of non-gray pixels (4)");
11136
11137                       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11138                           "  ping_pixels[0]=%d,ping_pixels[1]=%d",
11139                           (int)ping_pixels[0],(int)ping_pixels[1]);
11140                     }
11141                   }
11142                 png_write_row(ping,ping_pixels);
11143
11144                 status=SetImageProgress(image,SaveImageTag,
11145                   (MagickOffsetType) (pass * image->rows + y),
11146                   num_passes * image->rows);
11147
11148                 if (status == MagickFalse)
11149                   break;
11150               }
11151             }
11152           }
11153         }
11154     }
11155
11156   if (quantum_info != (QuantumInfo *) NULL)
11157     quantum_info=DestroyQuantumInfo(quantum_info);
11158
11159   if (logging != MagickFalse)
11160     {
11161       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11162         "  Wrote PNG image data");
11163
11164       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11165         "    Width: %.20g",(double) ping_width);
11166
11167       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11168         "    Height: %.20g",(double) ping_height);
11169
11170       if (mng_info->write_png_depth)
11171         {
11172           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11173             "    Defined png:bit-depth: %d",mng_info->write_png_depth);
11174         }
11175
11176       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11177         "    PNG bit-depth written: %d",ping_bit_depth);
11178
11179       if (mng_info->write_png_colortype)
11180         {
11181           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11182             "    Defined png:color-type: %d",mng_info->write_png_colortype-1);
11183         }
11184
11185       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11186         "    PNG color-type written: %d",ping_color_type);
11187
11188       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11189         "    PNG Interlace method: %d",ping_interlace_method);
11190     }
11191   /*
11192     Generate text chunks after IDAT.
11193   */
11194   if (ping_exclude_tEXt == MagickFalse || ping_exclude_zTXt == MagickFalse)
11195   {
11196     ResetImagePropertyIterator(image);
11197     property=GetNextImageProperty(image);
11198     while (property != (const char *) NULL)
11199     {
11200       png_textp
11201         text;
11202
11203       value=GetImageProperty(image,property,exception);
11204
11205       /* Don't write any "png:" or "jpeg:" properties; those are just for
11206        * "identify" or for passing through to another JPEG
11207        */
11208       if ((LocaleNCompare(property,"png:",4) != 0 &&
11209            LocaleNCompare(property,"jpeg:",5) != 0) &&
11210
11211
11212           /* Suppress density and units if we wrote a pHYs chunk */
11213           (ping_exclude_pHYs != MagickFalse      ||
11214           LocaleCompare(property,"density") != 0 ||
11215           LocaleCompare(property,"units") != 0) &&
11216
11217           /* Suppress the IM-generated Date:create and Date:modify */
11218           (ping_exclude_date == MagickFalse      ||
11219           LocaleNCompare(property, "Date:",5) != 0))
11220         {
11221         if (value != (const char *) NULL)
11222           {
11223
11224 #if PNG_LIBPNG_VER >= 10400
11225             text=(png_textp) png_malloc(ping,
11226                  (png_alloc_size_t) sizeof(png_text));
11227 #else
11228             text=(png_textp) png_malloc(ping,(png_size_t) sizeof(png_text));
11229 #endif
11230             text[0].key=(char *) property;
11231             text[0].text=(char *) value;
11232             text[0].text_length=strlen(value);
11233
11234             if (ping_exclude_tEXt != MagickFalse)
11235                text[0].compression=PNG_TEXT_COMPRESSION_zTXt;
11236
11237             else if (ping_exclude_zTXt != MagickFalse)
11238                text[0].compression=PNG_TEXT_COMPRESSION_NONE;
11239
11240             else
11241             {
11242                text[0].compression=image_info->compression == NoCompression ||
11243                  (image_info->compression == UndefinedCompression &&
11244                  text[0].text_length < 128) ? PNG_TEXT_COMPRESSION_NONE :
11245                  PNG_TEXT_COMPRESSION_zTXt ;
11246             }
11247
11248             if (logging != MagickFalse)
11249               {
11250                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11251                   "  Setting up text chunk");
11252
11253                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11254                   "    keyword: '%s'",text[0].key);
11255               }
11256
11257             png_set_text(ping,ping_info,text,1);
11258             png_free(ping,text);
11259           }
11260         }
11261       property=GetNextImageProperty(image);
11262     }
11263   }
11264
11265   /* write any PNG-chunk-e profiles */
11266   (void) Magick_png_write_chunk_from_profile(image,"PNG-chunk-e",logging);
11267
11268   if (logging != MagickFalse)
11269     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11270       "  Writing PNG end info");
11271
11272   png_write_end(ping,ping_info);
11273
11274   if (mng_info->need_fram && (int) image->dispose == BackgroundDispose)
11275     {
11276       if (mng_info->page.x || mng_info->page.y ||
11277           (ping_width != mng_info->page.width) ||
11278           (ping_height != mng_info->page.height))
11279         {
11280           unsigned char
11281             chunk[32];
11282
11283           /*
11284             Write FRAM 4 with clipping boundaries followed by FRAM 1.
11285           */
11286           (void) WriteBlobMSBULong(image,27L);  /* data length=27 */
11287           PNGType(chunk,mng_FRAM);
11288           LogPNGChunk(logging,mng_FRAM,27L);
11289           chunk[4]=4;
11290           chunk[5]=0;  /* frame name separator (no name) */
11291           chunk[6]=1;  /* flag for changing delay, for next frame only */
11292           chunk[7]=0;  /* flag for changing frame timeout */
11293           chunk[8]=1;  /* flag for changing frame clipping for next frame */
11294           chunk[9]=0;  /* flag for changing frame sync_id */
11295           PNGLong(chunk+10,(png_uint_32) (0L)); /* temporary 0 delay */
11296           chunk[14]=0; /* clipping boundaries delta type */
11297           PNGLong(chunk+15,(png_uint_32) (mng_info->page.x)); /* left cb */
11298           PNGLong(chunk+19,
11299              (png_uint_32) (mng_info->page.x + ping_width));
11300           PNGLong(chunk+23,(png_uint_32) (mng_info->page.y)); /* top cb */
11301           PNGLong(chunk+27,
11302              (png_uint_32) (mng_info->page.y + ping_height));
11303           (void) WriteBlob(image,31,chunk);
11304           (void) WriteBlobMSBULong(image,crc32(0,chunk,31));
11305           mng_info->old_framing_mode=4;
11306           mng_info->framing_mode=1;
11307         }
11308
11309       else
11310         mng_info->framing_mode=3;
11311     }
11312   if (mng_info->write_mng && !mng_info->need_fram &&
11313       ((int) image->dispose == 3))
11314      png_error(ping, "Cannot convert GIF with disposal method 3 to MNG-LC");
11315
11316   /*
11317     Free PNG resources.
11318   */
11319
11320   png_destroy_write_struct(&ping,&ping_info);
11321
11322   pixel_info=RelinquishVirtualMemory(pixel_info);
11323
11324   if (ping_have_blob != MagickFalse)
11325      (void) CloseBlob(image);
11326
11327   image_info=DestroyImageInfo(image_info);
11328   image=DestroyImage(image);
11329
11330   /* Store bit depth actually written */
11331   s[0]=(char) ping_bit_depth;
11332   s[1]='\0';
11333
11334   (void) SetImageProperty(IMimage,"png:bit-depth-written",s,exception);
11335
11336   if (logging != MagickFalse)
11337     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11338       "  exit WriteOnePNGImage()");
11339
11340 #ifdef IMPNG_SETJMP_NOT_THREAD_SAFE
11341   UnlockSemaphoreInfo(ping_semaphore);
11342 #endif
11343
11344    /* }  for navigation to beginning of SETJMP-protected block. Revert to
11345     *    Throwing an Exception when an error occurs.
11346     */
11347
11348   return(MagickTrue);
11349 /*  End write one PNG image */
11350
11351 }
11352
11353 /*
11354 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11355 %                                                                             %
11356 %                                                                             %
11357 %                                                                             %
11358 %   W r i t e P N G I m a g e                                                 %
11359 %                                                                             %
11360 %                                                                             %
11361 %                                                                             %
11362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11363 %
11364 %  WritePNGImage() writes a Portable Network Graphics (PNG) or
11365 %  Multiple-image Network Graphics (MNG) image file.
11366 %
11367 %  MNG support written by Glenn Randers-Pehrson, glennrp@image...
11368 %
11369 %  The format of the WritePNGImage method is:
11370 %
11371 %      MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11372 %        Image *image,ExceptionInfo *exception)
11373 %
11374 %  A description of each parameter follows:
11375 %
11376 %    o image_info: the image info.
11377 %
11378 %    o image:  The image.
11379 %
11380 %    o exception: return any errors or warnings in this structure.
11381 %
11382 %  Returns MagickTrue on success, MagickFalse on failure.
11383 %
11384 %  Communicating with the PNG encoder:
11385 %
11386 %  While the datastream written is always in PNG format and normally would
11387 %  be given the "png" file extension, this method also writes the following
11388 %  pseudo-formats which are subsets of png:
11389 %
11390 %    o PNG8:    An 8-bit indexed PNG datastream is written.  If the image has
11391 %               a depth greater than 8, the depth is reduced. If transparency
11392 %               is present, the tRNS chunk must only have values 0 and 255
11393 %               (i.e., transparency is binary: fully opaque or fully
11394 %               transparent).  If other values are present they will be
11395 %               50%-thresholded to binary transparency.  If more than 256
11396 %               colors are present, they will be quantized to the 4-4-4-1,
11397 %               3-3-3-1, or 3-3-2-1 palette.  The underlying RGB color
11398 %               of any resulting fully-transparent pixels is changed to
11399 %               the image's background color.
11400 %
11401 %               If you want better quantization or dithering of the colors
11402 %               or alpha than that, you need to do it before calling the
11403 %               PNG encoder. The pixels contain 8-bit indices even if
11404 %               they could be represented with 1, 2, or 4 bits.  Grayscale
11405 %               images will be written as indexed PNG files even though the
11406 %               PNG grayscale type might be slightly more efficient.  Please
11407 %               note that writing to the PNG8 format may result in loss
11408 %               of color and alpha data.
11409 %
11410 %    o PNG24:   An 8-bit per sample RGB PNG datastream is written.  The tRNS
11411 %               chunk can be present to convey binary transparency by naming
11412 %               one of the colors as transparent.  The only loss incurred
11413 %               is reduction of sample depth to 8.  If the image has more
11414 %               than one transparent color, has semitransparent pixels, or
11415 %               has an opaque pixel with the same RGB components as the
11416 %               transparent color, an image is not written.
11417 %
11418 %    o PNG32:   An 8-bit per sample RGBA PNG is written.  Partial
11419 %               transparency is permitted, i.e., the alpha sample for
11420 %               each pixel can have any value from 0 to 255. The alpha
11421 %               channel is present even if the image is fully opaque.
11422 %               The only loss in data is the reduction of the sample depth
11423 %               to 8.
11424 %
11425 %    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
11426 %               chunk can be present to convey binary transparency by naming
11427 %               one of the colors as transparent.  If the image has more
11428 %               than one transparent color, has semitransparent pixels, or
11429 %               has an opaque pixel with the same RGB components as the
11430 %               transparent color, an image is not written.
11431 %
11432 %    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
11433 %               transparency is permitted, i.e., the alpha sample for
11434 %               each pixel can have any value from 0 to 65535. The alpha
11435 %               channel is present even if the image is fully opaque.
11436 %
11437 %    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
11438 %               image, if the input was a PNG, is written.  If these values
11439 %               cannot be found, or if the pixels have been changed in a way
11440 %               that makes this impossible, then "PNG00" falls back to the
11441 %               regular "PNG" format.
11442 %
11443 %    o -define: For more precise control of the PNG output, you can use the
11444 %               Image options "png:bit-depth" and "png:color-type".  These
11445 %               can be set from the commandline with "-define" and also
11446 %               from the application programming interfaces.  The options
11447 %               are case-independent and are converted to lowercase before
11448 %               being passed to this encoder.
11449 %
11450 %               png:color-type can be 0, 2, 3, 4, or 6.
11451 %
11452 %               When png:color-type is 0 (Grayscale), png:bit-depth can
11453 %               be 1, 2, 4, 8, or 16.
11454 %
11455 %               When png:color-type is 2 (RGB), png:bit-depth can
11456 %               be 8 or 16.
11457 %
11458 %               When png:color-type is 3 (Indexed), png:bit-depth can
11459 %               be 1, 2, 4, or 8.  This refers to the number of bits
11460 %               used to store the index.  The color samples always have
11461 %               bit-depth 8 in indexed PNG files.
11462 %
11463 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
11464 %               png:bit-depth can be 8 or 16.
11465 %
11466 %               If the image cannot be written without loss with the
11467 %               requested bit-depth and color-type, a PNG file will not
11468 %               be written, a warning will be issued, and the encoder will
11469 %               return MagickFalse.
11470 %
11471 %  Since image encoders should not be responsible for the "heavy lifting",
11472 %  the user should make sure that ImageMagick has already reduced the
11473 %  image depth and number of colors and limit transparency to binary
11474 %  transparency prior to attempting to write the image with depth, color,
11475 %  or transparency limitations.
11476 %
11477 %  Note that another definition, "png:bit-depth-written" exists, but it
11478 %  is not intended for external use.  It is only used internally by the
11479 %  PNG encoder to inform the JNG encoder of the depth of the alpha channel.
11480 %
11481 %  It is possible to request that the PNG encoder write previously-formatted
11482 %  ancillary chunks in the output PNG file, using the "-profile" commandline
11483 %  option as shown below or by setting the profile via a programming
11484 %  interface:
11485 %
11486 %     -profile PNG-chunk-x:<file>
11487 %
11488 %  where x is a location flag and <file> is a file containing the chunk
11489 %  name in the first 4 bytes, then a colon (":"), followed by the chunk data.
11490 %  This encoder will compute the chunk length and CRC, so those must not
11491 %  be included in the file.
11492 %
11493 %  "x" can be "b" (before PLTE), "m" (middle, i.e., between PLTE and IDAT),
11494 %  or "e" (end, i.e., after IDAT).  If you want to write multiple chunks
11495 %  of the same type, then add a short unique string after the "x" to prevent
11496 %  subsequent profiles from overwriting the preceding ones, e.g.,
11497 %
11498 %     -profile PNG-chunk-b01:file01 -profile PNG-chunk-b02:file02
11499 %
11500 %  As of version 6.6.6 the following optimizations are always done:
11501 %
11502 %   o  32-bit depth is reduced to 16.
11503 %   o  16-bit depth is reduced to 8 if all pixels contain samples whose
11504 %      high byte and low byte are identical.
11505 %   o  Palette is sorted to remove unused entries and to put a
11506 %      transparent color first, if BUILD_PNG_PALETTE is defined.
11507 %   o  Opaque matte channel is removed when writing an indexed PNG.
11508 %   o  Grayscale images are reduced to 1, 2, or 4 bit depth if
11509 %      this can be done without loss and a larger bit depth N was not
11510 %      requested via the "-define png:bit-depth=N" option.
11511 %   o  If matte channel is present but only one transparent color is
11512 %      present, RGB+tRNS is written instead of RGBA
11513 %   o  Opaque matte channel is removed (or added, if color-type 4 or 6
11514 %      was requested when converting an opaque image).
11515 %
11516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11517 */
11518 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
11519   Image *image,ExceptionInfo *exception)
11520 {
11521   MagickBooleanType
11522     excluding,
11523     logging,
11524     have_mng_structure,
11525     status;
11526
11527   MngInfo
11528     *mng_info;
11529
11530   const char
11531     *value;
11532
11533   int
11534     source;
11535
11536   /*
11537     Open image file.
11538   */
11539   assert(image_info != (const ImageInfo *) NULL);
11540   assert(image_info->signature == MagickCoreSignature);
11541   assert(image != (Image *) NULL);
11542   assert(image->signature == MagickCoreSignature);
11543   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
11544   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WritePNGImage()");
11545   /*
11546     Allocate a MngInfo structure.
11547   */
11548   have_mng_structure=MagickFalse;
11549   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
11550
11551   if (mng_info == (MngInfo *) NULL)
11552     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
11553
11554   /*
11555     Initialize members of the MngInfo structure.
11556   */
11557   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
11558   mng_info->image=image;
11559   mng_info->equal_backgrounds=MagickTrue;
11560   have_mng_structure=MagickTrue;
11561
11562   /* See if user has requested a specific PNG subformat */
11563
11564   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
11565   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
11566   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
11567   mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
11568   mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
11569
11570   value=GetImageOption(image_info,"png:format");
11571
11572   if (value != (char *) NULL || LocaleCompare(image_info->magick,"PNG00") == 0)
11573     {
11574       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11575          "  Format=%s",value);
11576
11577       mng_info->write_png8 = MagickFalse;
11578       mng_info->write_png24 = MagickFalse;
11579       mng_info->write_png32 = MagickFalse;
11580       mng_info->write_png48 = MagickFalse;
11581       mng_info->write_png64 = MagickFalse;
11582
11583       if (LocaleCompare(value,"png8") == 0)
11584         mng_info->write_png8 = MagickTrue;
11585
11586       else if (LocaleCompare(value,"png24") == 0)
11587         mng_info->write_png24 = MagickTrue;
11588
11589       else if (LocaleCompare(value,"png32") == 0)
11590         mng_info->write_png32 = MagickTrue;
11591
11592       else if (LocaleCompare(value,"png48") == 0)
11593         mng_info->write_png48 = MagickTrue;
11594
11595       else if (LocaleCompare(value,"png64") == 0)
11596         mng_info->write_png64 = MagickTrue;
11597
11598       else if ((LocaleCompare(value,"png00") == 0) ||
11599          LocaleCompare(image_info->magick,"PNG00") == 0)
11600         {
11601           /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig. */
11602           value=GetImageProperty(image,"png:IHDR.bit-depth-orig",exception);
11603
11604           if (value != (char *) NULL)
11605             {
11606               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11607                  "  png00 inherited bit depth=%s",value);
11608
11609               if (LocaleCompare(value,"1") == 0)
11610                 mng_info->write_png_depth = 1;
11611
11612               else if (LocaleCompare(value,"2") == 0)
11613                 mng_info->write_png_depth = 2;
11614
11615               else if (LocaleCompare(value,"4") == 0)
11616                 mng_info->write_png_depth = 4;
11617
11618               else if (LocaleCompare(value,"8") == 0)
11619                 mng_info->write_png_depth = 8;
11620
11621               else if (LocaleCompare(value,"16") == 0)
11622                 mng_info->write_png_depth = 16;
11623             }
11624
11625           value=GetImageProperty(image,"png:IHDR.color-type-orig",exception);
11626
11627           if (value != (char *) NULL)
11628             {
11629               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11630                  "  png00 inherited color type=%s",value);
11631
11632               if (LocaleCompare(value,"0") == 0)
11633                 mng_info->write_png_colortype = 1;
11634
11635               else if (LocaleCompare(value,"2") == 0)
11636                 mng_info->write_png_colortype = 3;
11637
11638               else if (LocaleCompare(value,"3") == 0)
11639                 mng_info->write_png_colortype = 4;
11640
11641               else if (LocaleCompare(value,"4") == 0)
11642                 mng_info->write_png_colortype = 5;
11643
11644               else if (LocaleCompare(value,"6") == 0)
11645                 mng_info->write_png_colortype = 7;
11646             }
11647         }
11648     }
11649
11650   if (mng_info->write_png8)
11651     {
11652       mng_info->write_png_colortype = /* 3 */ 4;
11653       mng_info->write_png_depth = 8;
11654       image->depth = 8;
11655     }
11656
11657   if (mng_info->write_png24)
11658     {
11659       mng_info->write_png_colortype = /* 2 */ 3;
11660       mng_info->write_png_depth = 8;
11661       image->depth = 8;
11662
11663       if (image->alpha_trait != UndefinedPixelTrait)
11664         (void) SetImageType(image,TrueColorAlphaType,exception);
11665
11666       else
11667         (void) SetImageType(image,TrueColorType,exception);
11668
11669       (void) SyncImage(image,exception);
11670     }
11671
11672   if (mng_info->write_png32)
11673     {
11674       mng_info->write_png_colortype = /* 6 */  7;
11675       mng_info->write_png_depth = 8;
11676       image->depth = 8;
11677       image->alpha_trait = BlendPixelTrait;
11678
11679       (void) SetImageType(image,TrueColorAlphaType,exception);
11680       (void) SyncImage(image,exception);
11681     }
11682
11683   if (mng_info->write_png48)
11684     {
11685       mng_info->write_png_colortype = /* 2 */ 3;
11686       mng_info->write_png_depth = 16;
11687       image->depth = 16;
11688
11689       if (image->alpha_trait != UndefinedPixelTrait)
11690         (void) SetImageType(image,TrueColorAlphaType,exception);
11691
11692       else
11693         (void) SetImageType(image,TrueColorType,exception);
11694
11695       (void) SyncImage(image,exception);
11696     }
11697
11698   if (mng_info->write_png64)
11699     {
11700       mng_info->write_png_colortype = /* 6 */  7;
11701       mng_info->write_png_depth = 16;
11702       image->depth = 16;
11703       image->alpha_trait = BlendPixelTrait;
11704
11705       (void) SetImageType(image,TrueColorAlphaType,exception);
11706       (void) SyncImage(image,exception);
11707     }
11708
11709   value=GetImageOption(image_info,"png:bit-depth");
11710
11711   if (value != (char *) NULL)
11712     {
11713       if (LocaleCompare(value,"1") == 0)
11714         mng_info->write_png_depth = 1;
11715
11716       else if (LocaleCompare(value,"2") == 0)
11717         mng_info->write_png_depth = 2;
11718
11719       else if (LocaleCompare(value,"4") == 0)
11720         mng_info->write_png_depth = 4;
11721
11722       else if (LocaleCompare(value,"8") == 0)
11723         mng_info->write_png_depth = 8;
11724
11725       else if (LocaleCompare(value,"16") == 0)
11726         mng_info->write_png_depth = 16;
11727
11728       else
11729         (void) ThrowMagickException(exception,
11730              GetMagickModule(),CoderWarning,
11731              "ignoring invalid defined png:bit-depth",
11732              "=%s",value);
11733
11734       if (logging != MagickFalse)
11735         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11736           "  png:bit-depth=%d was defined.\n",mng_info->write_png_depth);
11737     }
11738
11739   value=GetImageOption(image_info,"png:color-type");
11740
11741   if (value != (char *) NULL)
11742     {
11743       /* We must store colortype+1 because 0 is a valid colortype */
11744       if (LocaleCompare(value,"0") == 0)
11745         mng_info->write_png_colortype = 1;
11746
11747       else if (LocaleCompare(value,"1") == 0)
11748         mng_info->write_png_colortype = 2;
11749
11750       else if (LocaleCompare(value,"2") == 0)
11751         mng_info->write_png_colortype = 3;
11752
11753       else if (LocaleCompare(value,"3") == 0)
11754         mng_info->write_png_colortype = 4;
11755
11756       else if (LocaleCompare(value,"4") == 0)
11757         mng_info->write_png_colortype = 5;
11758
11759       else if (LocaleCompare(value,"6") == 0)
11760         mng_info->write_png_colortype = 7;
11761
11762       else
11763         (void) ThrowMagickException(exception,
11764              GetMagickModule(),CoderWarning,
11765              "ignoring invalid defined png:color-type",
11766              "=%s",value);
11767
11768       if (logging != MagickFalse)
11769         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
11770           "  png:color-type=%d was defined.\n",mng_info->write_png_colortype-1);
11771     }
11772
11773   /* Check for chunks to be excluded:
11774    *
11775    * The default is to not exclude any known chunks except for any
11776    * listed in the "unused_chunks" array, above.
11777    *
11778    * Chunks can be listed for exclusion via a "png:exclude-chunk"
11779    * define (in the image properties or in the image artifacts)
11780    * or via a mng_info member.  For convenience, in addition
11781    * to or instead of a comma-separated list of chunks, the
11782    * "exclude-chunk" string can be simply "all" or "none".
11783    *
11784    * The exclude-chunk define takes priority over the mng_info.
11785    *
11786    * A "png:include-chunk" define takes  priority over both the
11787    * mng_info and the "png:exclude-chunk" define.  Like the
11788    * "exclude-chunk" string, it can define "all" or "none" as
11789    * well as a comma-separated list.  Chunks that are unknown to
11790    * ImageMagick are always excluded, regardless of their "copy-safe"
11791    * status according to the PNG specification, and even if they
11792    * appear in the "include-chunk" list. Such defines appearing among
11793    * the image options take priority over those found among the image
11794    * artifacts.
11795    *
11796    * Finally, all chunks listed in the "unused_chunks" array are
11797    * automatically excluded, regardless of the other instructions
11798    * or lack thereof.
11799    *
11800    * if you exclude sRGB but not gAMA (recommended), then sRGB chunk
11801    * will not be written and the gAMA chunk will only be written if it
11802    * is not between .45 and .46, or approximately (1.0/2.2).
11803    *
11804    * If you exclude tRNS and the image has transparency, the colortype
11805    * is forced to be 4 or 6 (GRAY_ALPHA or RGB_ALPHA).
11806    *
11807    * The -strip option causes StripImage() to set the png:include-chunk
11808    * artifact to "none,trns,gama".
11809    */
11810
11811   mng_info->ping_exclude_bKGD=MagickFalse;
11812   mng_info->ping_exclude_cHRM=MagickFalse;
11813   mng_info->ping_exclude_date=MagickFalse;
11814   mng_info->ping_exclude_EXIF=MagickFalse; /* hex-encoded EXIF in zTXt */
11815   mng_info->ping_exclude_gAMA=MagickFalse;
11816   mng_info->ping_exclude_iCCP=MagickFalse;
11817   /* mng_info->ping_exclude_iTXt=MagickFalse; */
11818   mng_info->ping_exclude_oFFs=MagickFalse;
11819   mng_info->ping_exclude_pHYs=MagickFalse;
11820   mng_info->ping_exclude_sRGB=MagickFalse;
11821   mng_info->ping_exclude_tEXt=MagickFalse;
11822   mng_info->ping_exclude_tIME=MagickFalse;
11823   mng_info->ping_exclude_tRNS=MagickFalse;
11824   mng_info->ping_exclude_vpAg=MagickFalse;
11825   mng_info->ping_exclude_zCCP=MagickFalse; /* hex-encoded iCCP in zTXt */
11826   mng_info->ping_exclude_zTXt=MagickFalse;
11827
11828   mng_info->ping_preserve_colormap=MagickFalse;
11829
11830   value=GetImageOption(image_info,"png:preserve-colormap");
11831   if (value == NULL)
11832      value=GetImageArtifact(image,"png:preserve-colormap");
11833   if (value != NULL)
11834      mng_info->ping_preserve_colormap=MagickTrue;
11835
11836   mng_info->ping_preserve_iCCP=MagickFalse;
11837
11838   value=GetImageOption(image_info,"png:preserve-iCCP");
11839   if (value == NULL)
11840      value=GetImageArtifact(image,"png:preserve-iCCP");
11841   if (value != NULL)
11842      mng_info->ping_preserve_iCCP=MagickTrue;
11843
11844   /* These compression-level, compression-strategy, and compression-filter
11845    * defines take precedence over values from the -quality option.
11846    */
11847   value=GetImageOption(image_info,"png:compression-level");
11848   if (value == NULL)
11849      value=GetImageArtifact(image,"png:compression-level");
11850   if (value != NULL)
11851   {
11852       /* We have to add 1 to everything because 0 is a valid input,
11853        * and we want to use 0 (the default) to mean undefined.
11854        */
11855       if (LocaleCompare(value,"0") == 0)
11856         mng_info->write_png_compression_level = 1;
11857
11858       else if (LocaleCompare(value,"1") == 0)
11859         mng_info->write_png_compression_level = 2;
11860
11861       else if (LocaleCompare(value,"2") == 0)
11862         mng_info->write_png_compression_level = 3;
11863
11864       else if (LocaleCompare(value,"3") == 0)
11865         mng_info->write_png_compression_level = 4;
11866
11867       else if (LocaleCompare(value,"4") == 0)
11868         mng_info->write_png_compression_level = 5;
11869
11870       else if (LocaleCompare(value,"5") == 0)
11871         mng_info->write_png_compression_level = 6;
11872
11873       else if (LocaleCompare(value,"6") == 0)
11874         mng_info->write_png_compression_level = 7;
11875
11876       else if (LocaleCompare(value,"7") == 0)
11877         mng_info->write_png_compression_level = 8;
11878
11879       else if (LocaleCompare(value,"8") == 0)
11880         mng_info->write_png_compression_level = 9;
11881
11882       else if (LocaleCompare(value,"9") == 0)
11883         mng_info->write_png_compression_level = 10;
11884
11885       else
11886         (void) ThrowMagickException(exception,
11887              GetMagickModule(),CoderWarning,
11888              "ignoring invalid defined png:compression-level",
11889              "=%s",value);
11890     }
11891
11892   value=GetImageOption(image_info,"png:compression-strategy");
11893   if (value == NULL)
11894      value=GetImageArtifact(image,"png:compression-strategy");
11895   if (value != NULL)
11896   {
11897       if (LocaleCompare(value,"0") == 0)
11898         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11899
11900       else if (LocaleCompare(value,"1") == 0)
11901         mng_info->write_png_compression_strategy = Z_FILTERED+1;
11902
11903       else if (LocaleCompare(value,"2") == 0)
11904         mng_info->write_png_compression_strategy = Z_HUFFMAN_ONLY+1;
11905
11906       else if (LocaleCompare(value,"3") == 0)
11907 #ifdef Z_RLE  /* Z_RLE was added to zlib-1.2.0 */
11908         mng_info->write_png_compression_strategy = Z_RLE+1;
11909 #else
11910         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11911 #endif
11912
11913       else if (LocaleCompare(value,"4") == 0)
11914 #ifdef Z_FIXED  /* Z_FIXED was added to zlib-1.2.2.2 */
11915         mng_info->write_png_compression_strategy = Z_FIXED+1;
11916 #else
11917         mng_info->write_png_compression_strategy = Z_DEFAULT_STRATEGY+1;
11918 #endif
11919
11920       else
11921         (void) ThrowMagickException(exception,
11922              GetMagickModule(),CoderWarning,
11923              "ignoring invalid defined png:compression-strategy",
11924              "=%s",value);
11925     }
11926
11927   value=GetImageOption(image_info,"png:compression-filter");
11928   if (value == NULL)
11929      value=GetImageArtifact(image,"png:compression-filter");
11930   if (value != NULL)
11931   {
11932       /* To do: combinations of filters allowed by libpng
11933        * masks 0x08 through 0xf8
11934        *
11935        * Implement this as a comma-separated list of 0,1,2,3,4,5
11936        * where 5 is a special case meaning PNG_ALL_FILTERS.
11937        */
11938
11939       if (LocaleCompare(value,"0") == 0)
11940         mng_info->write_png_compression_filter = 1;
11941
11942       else if (LocaleCompare(value,"1") == 0)
11943         mng_info->write_png_compression_filter = 2;
11944
11945       else if (LocaleCompare(value,"2") == 0)
11946         mng_info->write_png_compression_filter = 3;
11947
11948       else if (LocaleCompare(value,"3") == 0)
11949         mng_info->write_png_compression_filter = 4;
11950
11951       else if (LocaleCompare(value,"4") == 0)
11952         mng_info->write_png_compression_filter = 5;
11953
11954       else if (LocaleCompare(value,"5") == 0)
11955         mng_info->write_png_compression_filter = 6;
11956
11957       else
11958         (void) ThrowMagickException(exception,
11959              GetMagickModule(),CoderWarning,
11960              "ignoring invalid defined png:compression-filter",
11961              "=%s",value);
11962   }
11963
11964   for (source=0; source<8; source++)
11965   {
11966     value = NULL;
11967
11968     if (source == 0)
11969       value=GetImageOption(image_info,"png:exclude-chunks");
11970
11971     if (source == 1)
11972       value=GetImageArtifact(image,"png:exclude-chunks");
11973
11974     if (source == 2)
11975       value=GetImageOption(image_info,"png:exclude-chunk");
11976
11977     if (source == 3)
11978       value=GetImageArtifact(image,"png:exclude-chunk");
11979
11980     if (source == 4)
11981       value=GetImageOption(image_info,"png:include-chunks");
11982
11983     if (source == 5)
11984       value=GetImageArtifact(image,"png:include-chunks");
11985
11986     if (source == 6)
11987       value=GetImageOption(image_info,"png:include-chunk");
11988
11989     if (source == 7)
11990       value=GetImageArtifact(image,"png:include-chunk");
11991
11992     if (value == NULL)
11993        continue;
11994
11995     if (source < 4)
11996       excluding = MagickTrue;
11997     else
11998       excluding = MagickFalse;
11999
12000     if (logging != MagickFalse)
12001       {
12002         if (source == 0 || source == 2)
12003            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12004               "  png:exclude-chunk=%s found in image options.\n", value);
12005         else if (source == 1 || source == 3)
12006            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12007               "  png:exclude-chunk=%s found in image artifacts.\n", value);
12008         else if (source == 4 || source == 6)
12009            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12010               "  png:include-chunk=%s found in image options.\n", value);
12011         else /* if (source == 5 || source == 7) */
12012            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12013               "  png:include-chunk=%s found in image artifacts.\n", value);
12014       }
12015
12016     if (IsOptionMember("all",value) != MagickFalse)
12017       {
12018         mng_info->ping_exclude_bKGD=excluding;
12019         mng_info->ping_exclude_cHRM=excluding;
12020         mng_info->ping_exclude_date=excluding;
12021         mng_info->ping_exclude_EXIF=excluding;
12022         mng_info->ping_exclude_gAMA=excluding;
12023         mng_info->ping_exclude_iCCP=excluding;
12024         /* mng_info->ping_exclude_iTXt=excluding; */
12025         mng_info->ping_exclude_oFFs=excluding;
12026         mng_info->ping_exclude_pHYs=excluding;
12027         mng_info->ping_exclude_sRGB=excluding;
12028         mng_info->ping_exclude_tEXt=excluding;
12029         mng_info->ping_exclude_tIME=excluding;
12030         mng_info->ping_exclude_tRNS=excluding;
12031         mng_info->ping_exclude_vpAg=excluding;
12032         mng_info->ping_exclude_zCCP=excluding;
12033         mng_info->ping_exclude_zTXt=excluding;
12034       }
12035
12036     if (IsOptionMember("none",value) != MagickFalse)
12037       {
12038         mng_info->ping_exclude_bKGD=excluding != MagickFalse ? MagickFalse :
12039           MagickTrue;
12040         mng_info->ping_exclude_cHRM=excluding != MagickFalse ? MagickFalse :
12041           MagickTrue;
12042         mng_info->ping_exclude_date=excluding != MagickFalse ? MagickFalse :
12043           MagickTrue;
12044         mng_info->ping_exclude_EXIF=excluding != MagickFalse ? MagickFalse :
12045           MagickTrue;
12046         mng_info->ping_exclude_gAMA=excluding != MagickFalse ? MagickFalse :
12047           MagickTrue;
12048         mng_info->ping_exclude_iCCP=excluding != MagickFalse ? MagickFalse :
12049           MagickTrue;
12050         /* mng_info->ping_exclude_iTXt=!excluding; */
12051         mng_info->ping_exclude_oFFs=excluding != MagickFalse ? MagickFalse :
12052           MagickTrue;
12053         mng_info->ping_exclude_pHYs=excluding != MagickFalse ? MagickFalse :
12054           MagickTrue;
12055         mng_info->ping_exclude_sRGB=excluding != MagickFalse ? MagickFalse :
12056           MagickTrue;
12057         mng_info->ping_exclude_tEXt=excluding != MagickFalse ? MagickFalse :
12058           MagickTrue;
12059         mng_info->ping_exclude_tIME=excluding != MagickFalse ? MagickFalse :
12060           MagickTrue;
12061         mng_info->ping_exclude_tRNS=excluding != MagickFalse ? MagickFalse :
12062           MagickTrue;
12063         mng_info->ping_exclude_vpAg=excluding != MagickFalse ? MagickFalse :
12064           MagickTrue;
12065         mng_info->ping_exclude_zCCP=excluding != MagickFalse ? MagickFalse :
12066           MagickTrue;
12067         mng_info->ping_exclude_zTXt=excluding != MagickFalse ? MagickFalse :
12068           MagickTrue;
12069       }
12070
12071     if (IsOptionMember("bkgd",value) != MagickFalse)
12072       mng_info->ping_exclude_bKGD=excluding;
12073
12074     if (IsOptionMember("chrm",value) != MagickFalse)
12075       mng_info->ping_exclude_cHRM=excluding;
12076
12077     if (IsOptionMember("date",value) != MagickFalse)
12078       mng_info->ping_exclude_date=excluding;
12079
12080     if (IsOptionMember("exif",value) != MagickFalse)
12081       mng_info->ping_exclude_EXIF=excluding;
12082
12083     if (IsOptionMember("gama",value) != MagickFalse)
12084       mng_info->ping_exclude_gAMA=excluding;
12085
12086     if (IsOptionMember("iccp",value) != MagickFalse)
12087       mng_info->ping_exclude_iCCP=excluding;
12088
12089 #if 0
12090     if (IsOptionMember("itxt",value) != MagickFalse)
12091       mng_info->ping_exclude_iTXt=excluding;
12092 #endif
12093
12094     if (IsOptionMember("offs",value) != MagickFalse)
12095       mng_info->ping_exclude_oFFs=excluding;
12096
12097     if (IsOptionMember("phys",value) != MagickFalse)
12098       mng_info->ping_exclude_pHYs=excluding;
12099
12100     if (IsOptionMember("srgb",value) != MagickFalse)
12101       mng_info->ping_exclude_sRGB=excluding;
12102
12103     if (IsOptionMember("text",value) != MagickFalse)
12104       mng_info->ping_exclude_tEXt=excluding;
12105
12106     if (IsOptionMember("time",value) != MagickFalse)
12107       mng_info->ping_exclude_tIME=excluding;
12108
12109     if (IsOptionMember("trns",value) != MagickFalse)
12110       mng_info->ping_exclude_tRNS=excluding;
12111
12112     if (IsOptionMember("vpag",value) != MagickFalse)
12113       mng_info->ping_exclude_vpAg=excluding;
12114
12115     if (IsOptionMember("zccp",value) != MagickFalse)
12116       mng_info->ping_exclude_zCCP=excluding;
12117
12118     if (IsOptionMember("ztxt",value) != MagickFalse)
12119       mng_info->ping_exclude_zTXt=excluding;
12120   }
12121
12122   if (logging != MagickFalse)
12123   {
12124     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12125       "  Chunks to be excluded from the output png:");
12126     if (mng_info->ping_exclude_bKGD != MagickFalse)
12127       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12128           "    bKGD");
12129     if (mng_info->ping_exclude_cHRM != MagickFalse)
12130       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12131           "    cHRM");
12132     if (mng_info->ping_exclude_date != MagickFalse)
12133       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12134           "    date");
12135     if (mng_info->ping_exclude_EXIF != MagickFalse)
12136       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12137           "    EXIF");
12138     if (mng_info->ping_exclude_gAMA != MagickFalse)
12139       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12140           "    gAMA");
12141     if (mng_info->ping_exclude_iCCP != MagickFalse)
12142       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12143           "    iCCP");
12144 #if 0
12145     if (mng_info->ping_exclude_iTXt != MagickFalse)
12146       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12147           "    iTXt");
12148 #endif
12149
12150     if (mng_info->ping_exclude_oFFs != MagickFalse)
12151       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12152           "    oFFs");
12153     if (mng_info->ping_exclude_pHYs != MagickFalse)
12154       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12155           "    pHYs");
12156     if (mng_info->ping_exclude_sRGB != MagickFalse)
12157       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12158           "    sRGB");
12159     if (mng_info->ping_exclude_tEXt != MagickFalse)
12160       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12161           "    tEXt");
12162     if (mng_info->ping_exclude_tIME != MagickFalse)
12163       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12164           "    tIME");
12165     if (mng_info->ping_exclude_tRNS != MagickFalse)
12166       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12167           "    tRNS");
12168     if (mng_info->ping_exclude_vpAg != MagickFalse)
12169       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12170           "    vpAg");
12171     if (mng_info->ping_exclude_zCCP != MagickFalse)
12172       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12173           "    zCCP");
12174     if (mng_info->ping_exclude_zTXt != MagickFalse)
12175       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12176           "    zTXt");
12177   }
12178
12179   mng_info->need_blob = MagickTrue;
12180
12181   status=WriteOnePNGImage(mng_info,image_info,image,exception);
12182
12183   MngInfoFreeStruct(mng_info,&have_mng_structure);
12184
12185   if (logging != MagickFalse)
12186     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WritePNGImage()");
12187
12188   return(status);
12189 }
12190
12191 #if defined(JNG_SUPPORTED)
12192
12193 /* Write one JNG image */
12194 static MagickBooleanType WriteOneJNGImage(MngInfo *mng_info,
12195    const ImageInfo *image_info,Image *image,ExceptionInfo *exception)
12196 {
12197   Image
12198     *jpeg_image;
12199
12200   ImageInfo
12201     *jpeg_image_info;
12202
12203   MagickBooleanType
12204     logging,
12205     status;
12206
12207   size_t
12208     length;
12209
12210   unsigned char
12211     *blob,
12212     chunk[80],
12213     *p;
12214
12215   unsigned int
12216     jng_alpha_compression_method,
12217     jng_alpha_sample_depth,
12218     jng_color_type,
12219     transparent;
12220
12221   size_t
12222     jng_alpha_quality,
12223     jng_quality;
12224
12225   logging=LogMagickEvent(CoderEvent,GetMagickModule(),
12226     "  Enter WriteOneJNGImage()");
12227
12228   blob=(unsigned char *) NULL;
12229   jpeg_image=(Image *) NULL;
12230   jpeg_image_info=(ImageInfo *) NULL;
12231   length=0;
12232
12233   status=MagickTrue;
12234   transparent=image_info->type==GrayscaleAlphaType ||
12235      image_info->type==TrueColorAlphaType ||
12236      image->alpha_trait != UndefinedPixelTrait;
12237
12238   jng_alpha_sample_depth = 0;
12239
12240   jng_quality=image_info->quality == 0UL ? 75UL : image_info->quality%1000;
12241
12242   jng_alpha_compression_method=image->compression==JPEGCompression? 8 : 0;
12243
12244   jng_alpha_quality=image_info->quality == 0UL ? 75UL :
12245       image_info->quality;
12246
12247   if (jng_alpha_quality >= 1000)
12248     jng_alpha_quality /= 1000;
12249
12250   length=0;
12251
12252   if (transparent != 0)
12253     {
12254       jng_color_type=14;
12255
12256       /* Create JPEG blob, image, and image_info */
12257       if (logging != MagickFalse)
12258         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12259           "  Creating jpeg_image_info for alpha.");
12260
12261       jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12262
12263       if (jpeg_image_info == (ImageInfo *) NULL)
12264         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12265
12266       if (logging != MagickFalse)
12267         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12268           "  Creating jpeg_image.");
12269
12270       jpeg_image=SeparateImage(image,AlphaChannel,exception);
12271       if (jpeg_image == (Image *) NULL)
12272         ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12273       (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12274       jpeg_image->alpha_trait=UndefinedPixelTrait;
12275       jpeg_image->quality=jng_alpha_quality;
12276       jpeg_image_info->type=GrayscaleType;
12277       (void) SetImageType(jpeg_image,GrayscaleType,exception);
12278       (void) AcquireUniqueFilename(jpeg_image->filename);
12279       (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,
12280         "%s",jpeg_image->filename);
12281     }
12282   else
12283     {
12284       jng_alpha_compression_method=0;
12285       jng_color_type=10;
12286       jng_alpha_sample_depth=0;
12287     }
12288
12289   /* To do: check bit depth of PNG alpha channel */
12290
12291   /* Check if image is grayscale. */
12292   if (image_info->type != TrueColorAlphaType && image_info->type !=
12293     TrueColorType && SetImageGray(image,exception))
12294     jng_color_type-=2;
12295
12296   if (logging != MagickFalse)
12297     {
12298         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12299           "    JNG Quality           = %d",(int) jng_quality);
12300         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12301           "    JNG Color Type        = %d",jng_color_type);
12302         if (transparent != 0)
12303           {
12304             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12305               "    JNG Alpha Compression = %d",jng_alpha_compression_method);
12306             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12307               "    JNG Alpha Depth       = %d",jng_alpha_sample_depth);
12308             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12309               "    JNG Alpha Quality     = %d",(int) jng_alpha_quality);
12310           }
12311     }
12312
12313   if (transparent != 0)
12314     {
12315       if (jng_alpha_compression_method==0)
12316         {
12317           const char
12318             *value;
12319
12320           /* Encode alpha as a grayscale PNG blob */
12321           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12322             exception);
12323           if (status == MagickFalse)
12324             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12325  
12326           if (logging != MagickFalse)
12327             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12328               "  Creating PNG blob.");
12329
12330           (void) CopyMagickString(jpeg_image_info->magick,"PNG",MagickPathExtent);
12331           (void) CopyMagickString(jpeg_image->magick,"PNG",MagickPathExtent);
12332           jpeg_image_info->interlace=NoInterlace;
12333
12334           /* Exclude all ancillary chunks */
12335           (void) SetImageArtifact(jpeg_image,"png:exclude-chunks","all");
12336
12337           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,
12338             &length,exception);
12339
12340           /* Retrieve sample depth used */
12341           value=GetImageProperty(jpeg_image,"png:bit-depth-written",exception);
12342           if (value != (char *) NULL)
12343             jng_alpha_sample_depth= (unsigned int) value[0];
12344         }
12345       else
12346         {
12347           /* Encode alpha as a grayscale JPEG blob */
12348
12349           status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12350             exception);
12351           if (status == MagickFalse)
12352             ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12353  
12354
12355           (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12356           (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12357           jpeg_image_info->interlace=NoInterlace;
12358           if (logging != MagickFalse)
12359             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12360               "  Creating blob.");
12361           blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12362            exception);
12363           jng_alpha_sample_depth=8;
12364
12365           if (logging != MagickFalse)
12366             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12367               "  Successfully read jpeg_image into a blob, length=%.20g.",
12368               (double) length);
12369
12370         }
12371       /* Destroy JPEG image and image_info */
12372       jpeg_image=DestroyImage(jpeg_image);
12373       (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12374       jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12375     }
12376
12377   /* Write JHDR chunk */
12378   (void) WriteBlobMSBULong(image,16L);  /* chunk data length=16 */
12379   PNGType(chunk,mng_JHDR);
12380   LogPNGChunk(logging,mng_JHDR,16L);
12381   PNGLong(chunk+4,(png_uint_32) image->columns);
12382   PNGLong(chunk+8,(png_uint_32) image->rows);
12383   chunk[12]=jng_color_type;
12384   chunk[13]=8;  /* sample depth */
12385   chunk[14]=8; /*jng_image_compression_method */
12386   chunk[15]=(unsigned char) (image_info->interlace == NoInterlace ? 0 : 8);
12387   chunk[16]=jng_alpha_sample_depth;
12388   chunk[17]=jng_alpha_compression_method;
12389   chunk[18]=0; /*jng_alpha_filter_method */
12390   chunk[19]=0; /*jng_alpha_interlace_method */
12391   (void) WriteBlob(image,20,chunk);
12392   (void) WriteBlobMSBULong(image,crc32(0,chunk,20));
12393   if (logging != MagickFalse)
12394     {
12395       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12396         "    JNG width:%15lu",(unsigned long) image->columns);
12397
12398       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12399         "    JNG height:%14lu",(unsigned long) image->rows);
12400
12401       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12402         "    JNG color type:%10d",jng_color_type);
12403
12404       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12405         "    JNG sample depth:%8d",8);
12406
12407       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12408         "    JNG compression:%9d",8);
12409
12410       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12411         "    JNG interlace:%11d",0);
12412
12413       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12414         "    JNG alpha depth:%9d",jng_alpha_sample_depth);
12415
12416       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12417         "    JNG alpha compression:%3d",jng_alpha_compression_method);
12418
12419       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12420         "    JNG alpha filter:%8d",0);
12421
12422       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12423         "    JNG alpha interlace:%5d",0);
12424     }
12425
12426   /* Write any JNG-chunk-b profiles */
12427   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-b",logging);
12428
12429   /*
12430      Write leading ancillary chunks
12431   */
12432
12433   if (transparent != 0)
12434   {
12435     /*
12436       Write JNG bKGD chunk
12437     */
12438
12439     unsigned char
12440       blue,
12441       green,
12442       red;
12443
12444     ssize_t
12445       num_bytes;
12446
12447     if (jng_color_type == 8 || jng_color_type == 12)
12448       num_bytes=6L;
12449     else
12450       num_bytes=10L;
12451     (void) WriteBlobMSBULong(image,(size_t) (num_bytes-4L));
12452     PNGType(chunk,mng_bKGD);
12453     LogPNGChunk(logging,mng_bKGD,(size_t) (num_bytes-4L));
12454     red=ScaleQuantumToChar(image->background_color.red);
12455     green=ScaleQuantumToChar(image->background_color.green);
12456     blue=ScaleQuantumToChar(image->background_color.blue);
12457     *(chunk+4)=0;
12458     *(chunk+5)=red;
12459     *(chunk+6)=0;
12460     *(chunk+7)=green;
12461     *(chunk+8)=0;
12462     *(chunk+9)=blue;
12463     (void) WriteBlob(image,(size_t) num_bytes,chunk);
12464     (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) num_bytes));
12465   }
12466
12467   if ((image->colorspace == sRGBColorspace || image->rendering_intent))
12468     {
12469       /*
12470         Write JNG sRGB chunk
12471       */
12472       (void) WriteBlobMSBULong(image,1L);
12473       PNGType(chunk,mng_sRGB);
12474       LogPNGChunk(logging,mng_sRGB,1L);
12475
12476       if (image->rendering_intent != UndefinedIntent)
12477         chunk[4]=(unsigned char)
12478           Magick_RenderingIntent_to_PNG_RenderingIntent(
12479           (image->rendering_intent));
12480
12481       else
12482         chunk[4]=(unsigned char)
12483           Magick_RenderingIntent_to_PNG_RenderingIntent(
12484           (PerceptualIntent));
12485
12486       (void) WriteBlob(image,5,chunk);
12487       (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
12488     }
12489   else
12490     {
12491       if (image->gamma != 0.0)
12492         {
12493           /*
12494              Write JNG gAMA chunk
12495           */
12496           (void) WriteBlobMSBULong(image,4L);
12497           PNGType(chunk,mng_gAMA);
12498           LogPNGChunk(logging,mng_gAMA,4L);
12499           PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
12500           (void) WriteBlob(image,8,chunk);
12501           (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
12502         }
12503
12504       if ((mng_info->equal_chrms == MagickFalse) &&
12505           (image->chromaticity.red_primary.x != 0.0))
12506         {
12507           PrimaryInfo
12508             primary;
12509
12510           /*
12511              Write JNG cHRM chunk
12512           */
12513           (void) WriteBlobMSBULong(image,32L);
12514           PNGType(chunk,mng_cHRM);
12515           LogPNGChunk(logging,mng_cHRM,32L);
12516           primary=image->chromaticity.white_point;
12517           PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
12518           PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
12519           primary=image->chromaticity.red_primary;
12520           PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
12521           PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
12522           primary=image->chromaticity.green_primary;
12523           PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
12524           PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
12525           primary=image->chromaticity.blue_primary;
12526           PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
12527           PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
12528           (void) WriteBlob(image,36,chunk);
12529           (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
12530         }
12531     }
12532
12533   if (image->resolution.x && image->resolution.y && !mng_info->equal_physs)
12534     {
12535       /*
12536          Write JNG pHYs chunk
12537       */
12538       (void) WriteBlobMSBULong(image,9L);
12539       PNGType(chunk,mng_pHYs);
12540       LogPNGChunk(logging,mng_pHYs,9L);
12541       if (image->units == PixelsPerInchResolution)
12542         {
12543           PNGLong(chunk+4,(png_uint_32)
12544             (image->resolution.x*100.0/2.54+0.5));
12545
12546           PNGLong(chunk+8,(png_uint_32)
12547             (image->resolution.y*100.0/2.54+0.5));
12548
12549           chunk[12]=1;
12550         }
12551
12552       else
12553         {
12554           if (image->units == PixelsPerCentimeterResolution)
12555             {
12556               PNGLong(chunk+4,(png_uint_32)
12557                 (image->resolution.x*100.0+0.5));
12558
12559               PNGLong(chunk+8,(png_uint_32)
12560                 (image->resolution.y*100.0+0.5));
12561
12562               chunk[12]=1;
12563             }
12564
12565           else
12566             {
12567               PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
12568               PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
12569               chunk[12]=0;
12570             }
12571         }
12572       (void) WriteBlob(image,13,chunk);
12573       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12574     }
12575
12576   if (mng_info->write_mng == 0 && (image->page.x || image->page.y))
12577     {
12578       /*
12579          Write JNG oFFs chunk
12580       */
12581       (void) WriteBlobMSBULong(image,9L);
12582       PNGType(chunk,mng_oFFs);
12583       LogPNGChunk(logging,mng_oFFs,9L);
12584       PNGsLong(chunk+4,(ssize_t) (image->page.x));
12585       PNGsLong(chunk+8,(ssize_t) (image->page.y));
12586       chunk[12]=0;
12587       (void) WriteBlob(image,13,chunk);
12588       (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12589     }
12590   if (mng_info->write_mng == 0 && (image->page.width || image->page.height))
12591     {
12592        (void) WriteBlobMSBULong(image,9L);  /* data length=8 */
12593        PNGType(chunk,mng_vpAg);
12594        LogPNGChunk(logging,mng_vpAg,9L);
12595        PNGLong(chunk+4,(png_uint_32) image->page.width);
12596        PNGLong(chunk+8,(png_uint_32) image->page.height);
12597        chunk[12]=0;   /* unit = pixels */
12598        (void) WriteBlob(image,13,chunk);
12599        (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
12600     }
12601
12602   if (transparent != 0)
12603     {
12604       if (jng_alpha_compression_method==0)
12605         {
12606           register ssize_t
12607             i;
12608
12609           size_t
12610             len;
12611
12612           /* Write IDAT chunk header */
12613           if (logging != MagickFalse)
12614             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12615               "  Write IDAT chunks from blob, length=%.20g.",(double)
12616               length);
12617
12618           /* Copy IDAT chunks */
12619           len=0;
12620           p=blob+8;
12621           for (i=8; i<(ssize_t) length; i+=len+12)
12622           {
12623             len=(size_t) (*p) << 24;
12624             len|=(size_t) (*(p+1)) << 16;
12625             len|=(size_t) (*(p+2)) << 8;
12626             len|=(size_t) (*(p+3));
12627             p+=4;
12628
12629             if (*(p)==73 && *(p+1)==68 && *(p+2)==65 && *(p+3)==84) /* IDAT */
12630               {
12631                 /* Found an IDAT chunk. */
12632                 (void) WriteBlobMSBULong(image,len);
12633                 LogPNGChunk(logging,mng_IDAT,len);
12634                 (void) WriteBlob(image,len+4,p);
12635                 (void) WriteBlobMSBULong(image, crc32(0,p,(uInt) len+4));
12636               }
12637
12638             else
12639               {
12640                 if (logging != MagickFalse)
12641                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12642                     "    Skipping %c%c%c%c chunk, length=%.20g.",
12643                     *(p),*(p+1),*(p+2),*(p+3),(double) len);
12644               }
12645             p+=(8+len);
12646           }
12647         }
12648       else if (length != 0)
12649         {
12650           /* Write JDAA chunk header */
12651           if (logging != MagickFalse)
12652             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12653               "  Write JDAA chunk, length=%.20g.",(double) length);
12654           (void) WriteBlobMSBULong(image,(size_t) length);
12655           PNGType(chunk,mng_JDAA);
12656           LogPNGChunk(logging,mng_JDAA,length);
12657           /* Write JDAT chunk(s) data */
12658           (void) WriteBlob(image,4,chunk);
12659           (void) WriteBlob(image,length,blob);
12660           (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,
12661              (uInt) length));
12662         }
12663       blob=(unsigned char *) RelinquishMagickMemory(blob);
12664     }
12665
12666   /* Encode image as a JPEG blob */
12667   if (logging != MagickFalse)
12668     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12669       "  Creating jpeg_image_info.");
12670   jpeg_image_info=(ImageInfo *) CloneImageInfo(image_info);
12671   if (jpeg_image_info == (ImageInfo *) NULL)
12672     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12673
12674   if (logging != MagickFalse)
12675     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12676       "  Creating jpeg_image.");
12677
12678   jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
12679   if (jpeg_image == (Image *) NULL)
12680     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12681   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12682
12683   (void) AcquireUniqueFilename(jpeg_image->filename);
12684   (void) FormatLocaleString(jpeg_image_info->filename,MagickPathExtent,"%s",
12685     jpeg_image->filename);
12686
12687   status=OpenBlob(jpeg_image_info,jpeg_image,WriteBinaryBlobMode,
12688     exception);
12689
12690   if (logging != MagickFalse)
12691     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12692       "  Created jpeg_image, %.20g x %.20g.",(double) jpeg_image->columns,
12693       (double) jpeg_image->rows);
12694
12695   if (status == MagickFalse)
12696     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12697
12698   if (jng_color_type == 8 || jng_color_type == 12)
12699     jpeg_image_info->type=GrayscaleType;
12700
12701   jpeg_image_info->quality=jng_quality;
12702   jpeg_image->quality=jng_quality;
12703   (void) CopyMagickString(jpeg_image_info->magick,"JPEG",MagickPathExtent);
12704   (void) CopyMagickString(jpeg_image->magick,"JPEG",MagickPathExtent);
12705
12706   if (logging != MagickFalse)
12707     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12708       "  Creating blob.");
12709
12710   blob=(unsigned char *) ImageToBlob(jpeg_image_info,jpeg_image,&length,
12711     exception);
12712
12713   if (logging != MagickFalse)
12714     {
12715       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12716         "  Successfully read jpeg_image into a blob, length=%.20g.",
12717         (double) length);
12718
12719       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12720         "  Write JDAT chunk, length=%.20g.",(double) length);
12721     }
12722
12723   /* Write JDAT chunk(s) */
12724   (void) WriteBlobMSBULong(image,(size_t) length);
12725   PNGType(chunk,mng_JDAT);
12726   LogPNGChunk(logging,mng_JDAT,length);
12727   (void) WriteBlob(image,4,chunk);
12728   (void) WriteBlob(image,length,blob);
12729   (void) WriteBlobMSBULong(image,crc32(crc32(0,chunk,4),blob,(uInt) length));
12730
12731   jpeg_image=DestroyImage(jpeg_image);
12732   (void) RelinquishUniqueFileResource(jpeg_image_info->filename);
12733   jpeg_image_info=DestroyImageInfo(jpeg_image_info);
12734   blob=(unsigned char *) RelinquishMagickMemory(blob);
12735
12736   /* Write any JNG-chunk-e profiles */
12737   (void) Magick_png_write_chunk_from_profile(image,"JNG-chunk-e",logging);
12738
12739   /* Write IEND chunk */
12740   (void) WriteBlobMSBULong(image,0L);
12741   PNGType(chunk,mng_IEND);
12742   LogPNGChunk(logging,mng_IEND,0);
12743   (void) WriteBlob(image,4,chunk);
12744   (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
12745
12746   if (logging != MagickFalse)
12747     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12748       "  exit WriteOneJNGImage()");
12749
12750   return(status);
12751 }
12752
12753
12754 /*
12755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12756 %                                                                             %
12757 %                                                                             %
12758 %                                                                             %
12759 %   W r i t e J N G I m a g e                                                 %
12760 %                                                                             %
12761 %                                                                             %
12762 %                                                                             %
12763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12764 %
12765 %  WriteJNGImage() writes a JPEG Network Graphics (JNG) image file.
12766 %
12767 %  JNG support written by Glenn Randers-Pehrson, glennrp@image...
12768 %
12769 %  The format of the WriteJNGImage method is:
12770 %
12771 %      MagickBooleanType WriteJNGImage(const ImageInfo *image_info,
12772 %        Image *image,ExceptionInfo *exception)
12773 %
12774 %  A description of each parameter follows:
12775 %
12776 %    o image_info: the image info.
12777 %
12778 %    o image:  The image.
12779 %
12780 %    o exception: return any errors or warnings in this structure.
12781 %
12782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12783 */
12784 static MagickBooleanType WriteJNGImage(const ImageInfo *image_info,Image *image,
12785   ExceptionInfo *exception)
12786 {
12787   MagickBooleanType
12788     have_mng_structure,
12789     logging,
12790     status;
12791
12792   MngInfo
12793     *mng_info;
12794
12795   /*
12796     Open image file.
12797   */
12798   assert(image_info != (const ImageInfo *) NULL);
12799   assert(image_info->signature == MagickCoreSignature);
12800   assert(image != (Image *) NULL);
12801   assert(image->signature == MagickCoreSignature);
12802   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12803   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteJNGImage()");
12804   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12805   if (status == MagickFalse)
12806     return(status);
12807   if ((image->columns > 65535UL) || (image->rows > 65535UL))
12808     ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
12809
12810   /*
12811     Allocate a MngInfo structure.
12812   */
12813   have_mng_structure=MagickFalse;
12814   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12815   if (mng_info == (MngInfo *) NULL)
12816     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12817   /*
12818     Initialize members of the MngInfo structure.
12819   */
12820   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12821   mng_info->image=image;
12822   have_mng_structure=MagickTrue;
12823
12824   (void) WriteBlob(image,8,(const unsigned char *) "\213JNG\r\n\032\n");
12825
12826   status=WriteOneJNGImage(mng_info,image_info,image,exception);
12827   (void) CloseBlob(image);
12828
12829   (void) CatchImageException(image);
12830   MngInfoFreeStruct(mng_info,&have_mng_structure);
12831   if (logging != MagickFalse)
12832     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteJNGImage()");
12833   return(status);
12834 }
12835 #endif
12836
12837 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
12838   ExceptionInfo *exception)
12839 {
12840   const char
12841     *option;
12842
12843   Image
12844     *next_image;
12845
12846   MagickBooleanType
12847     have_mng_structure,
12848     status;
12849
12850   volatile MagickBooleanType
12851     logging;
12852
12853   MngInfo
12854     *mng_info;
12855
12856   int
12857     image_count,
12858     need_iterations,
12859     need_matte;
12860
12861   volatile int
12862 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
12863     defined(PNG_MNG_FEATURES_SUPPORTED)
12864     need_local_plte,
12865 #endif
12866     all_images_are_gray,
12867     need_defi,
12868     use_global_plte;
12869
12870   register ssize_t
12871     i;
12872
12873   unsigned char
12874     chunk[800];
12875
12876   volatile unsigned int
12877     write_jng,
12878     write_mng;
12879
12880   volatile size_t
12881     scene;
12882
12883   size_t
12884     final_delay=0,
12885     initial_delay;
12886
12887 #if (PNG_LIBPNG_VER < 10200)
12888     if (image_info->verbose)
12889       printf("Your PNG library (libpng-%s) is rather old.\n",
12890          PNG_LIBPNG_VER_STRING);
12891 #endif
12892
12893   /*
12894     Open image file.
12895   */
12896   assert(image_info != (const ImageInfo *) NULL);
12897   assert(image_info->signature == MagickCoreSignature);
12898   assert(image != (Image *) NULL);
12899   assert(image->signature == MagickCoreSignature);
12900   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
12901   logging=LogMagickEvent(CoderEvent,GetMagickModule(),"Enter WriteMNGImage()");
12902   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
12903   if (status == MagickFalse)
12904     return(status);
12905
12906   /*
12907     Allocate a MngInfo structure.
12908   */
12909   have_mng_structure=MagickFalse;
12910   mng_info=(MngInfo *) AcquireMagickMemory(sizeof(MngInfo));
12911   if (mng_info == (MngInfo *) NULL)
12912     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
12913   /*
12914     Initialize members of the MngInfo structure.
12915   */
12916   (void) ResetMagickMemory(mng_info,0,sizeof(MngInfo));
12917   mng_info->image=image;
12918   have_mng_structure=MagickTrue;
12919   write_mng=LocaleCompare(image_info->magick,"MNG") == 0;
12920
12921   /*
12922    * See if user has requested a specific PNG subformat to be used
12923    * for all of the PNGs in the MNG being written, e.g.,
12924    *
12925    *    convert *.png png8:animation.mng
12926    *
12927    * To do: check -define png:bit_depth and png:color_type as well,
12928    * or perhaps use mng:bit_depth and mng:color_type instead for
12929    * global settings.
12930    */
12931
12932   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
12933   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
12934   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
12935
12936   write_jng=MagickFalse;
12937   if (image_info->compression == JPEGCompression)
12938     write_jng=MagickTrue;
12939
12940   mng_info->adjoin=image_info->adjoin &&
12941     (GetNextImageInList(image) != (Image *) NULL) && write_mng;
12942
12943   if (logging != MagickFalse)
12944     {
12945       /* Log some info about the input */
12946       Image
12947         *p;
12948
12949       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12950         "  Checking input image(s)\n"
12951         "    Image_info depth: %.20g,    Type: %d",
12952         (double) image_info->depth, image_info->type);
12953
12954       scene=0;
12955       for (p=image; p != (Image *) NULL; p=GetNextImageInList(p))
12956       {
12957
12958         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12959            "    Scene: %.20g\n,   Image depth: %.20g",
12960            (double) scene++, (double) p->depth);
12961
12962         if (p->alpha_trait != UndefinedPixelTrait)
12963           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12964             "      Matte: True");
12965
12966         else
12967           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12968             "      Matte: False");
12969
12970         if (p->storage_class == PseudoClass)
12971           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12972             "      Storage class: PseudoClass");
12973
12974         else
12975           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12976             "      Storage class: DirectClass");
12977
12978         if (p->colors)
12979           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12980             "      Number of colors: %.20g",(double) p->colors);
12981
12982         else
12983           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
12984             "      Number of colors: unspecified");
12985
12986         if (mng_info->adjoin == MagickFalse)
12987           break;
12988       }
12989     }
12990
12991   use_global_plte=MagickFalse;
12992   all_images_are_gray=MagickFalse;
12993 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
12994   need_local_plte=MagickTrue;
12995 #endif
12996   need_defi=MagickFalse;
12997   need_matte=MagickFalse;
12998   mng_info->framing_mode=1;
12999   mng_info->old_framing_mode=1;
13000
13001   if (write_mng)
13002       if (image_info->page != (char *) NULL)
13003         {
13004           /*
13005             Determine image bounding box.
13006           */
13007           SetGeometry(image,&mng_info->page);
13008           (void) ParseMetaGeometry(image_info->page,&mng_info->page.x,
13009             &mng_info->page.y,&mng_info->page.width,&mng_info->page.height);
13010         }
13011   if (write_mng)
13012     {
13013       unsigned int
13014         need_geom;
13015
13016       unsigned short
13017         red,
13018         green,
13019         blue;
13020
13021       mng_info->page=image->page;
13022       need_geom=MagickTrue;
13023       if (mng_info->page.width || mng_info->page.height)
13024          need_geom=MagickFalse;
13025       /*
13026         Check all the scenes.
13027       */
13028       initial_delay=image->delay;
13029       need_iterations=MagickFalse;
13030       mng_info->equal_chrms=image->chromaticity.red_primary.x != 0.0;
13031       mng_info->equal_physs=MagickTrue,
13032       mng_info->equal_gammas=MagickTrue;
13033       mng_info->equal_srgbs=MagickTrue;
13034       mng_info->equal_backgrounds=MagickTrue;
13035       image_count=0;
13036 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13037     defined(PNG_MNG_FEATURES_SUPPORTED)
13038       all_images_are_gray=MagickTrue;
13039       mng_info->equal_palettes=MagickFalse;
13040       need_local_plte=MagickFalse;
13041 #endif
13042       for (next_image=image; next_image != (Image *) NULL; )
13043       {
13044         if (need_geom)
13045           {
13046             if ((next_image->columns+next_image->page.x) > mng_info->page.width)
13047               mng_info->page.width=next_image->columns+next_image->page.x;
13048
13049             if ((next_image->rows+next_image->page.y) > mng_info->page.height)
13050               mng_info->page.height=next_image->rows+next_image->page.y;
13051           }
13052
13053         if (next_image->page.x || next_image->page.y)
13054           need_defi=MagickTrue;
13055
13056         if (next_image->alpha_trait != UndefinedPixelTrait)
13057           need_matte=MagickTrue;
13058
13059         if ((int) next_image->dispose >= BackgroundDispose)
13060           if ((next_image->alpha_trait != UndefinedPixelTrait) ||
13061                next_image->page.x || next_image->page.y ||
13062               ((next_image->columns < mng_info->page.width) &&
13063                (next_image->rows < mng_info->page.height)))
13064             mng_info->need_fram=MagickTrue;
13065
13066         if (next_image->iterations)
13067           need_iterations=MagickTrue;
13068
13069         final_delay=next_image->delay;
13070
13071         if (final_delay != initial_delay || final_delay > 1UL*
13072            next_image->ticks_per_second)
13073           mng_info->need_fram=1;
13074
13075 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13076     defined(PNG_MNG_FEATURES_SUPPORTED)
13077         /*
13078           check for global palette possibility.
13079         */
13080         if (image->alpha_trait != UndefinedPixelTrait)
13081            need_local_plte=MagickTrue;
13082
13083         if (need_local_plte == 0)
13084           {
13085             if (SetImageGray(image,exception) == MagickFalse)
13086               all_images_are_gray=MagickFalse;
13087             mng_info->equal_palettes=PalettesAreEqual(image,next_image);
13088             if (use_global_plte == 0)
13089               use_global_plte=mng_info->equal_palettes;
13090             need_local_plte=!mng_info->equal_palettes;
13091           }
13092 #endif
13093         if (GetNextImageInList(next_image) != (Image *) NULL)
13094           {
13095             if (next_image->background_color.red !=
13096                 next_image->next->background_color.red ||
13097                 next_image->background_color.green !=
13098                 next_image->next->background_color.green ||
13099                 next_image->background_color.blue !=
13100                 next_image->next->background_color.blue)
13101               mng_info->equal_backgrounds=MagickFalse;
13102
13103             if (next_image->gamma != next_image->next->gamma)
13104               mng_info->equal_gammas=MagickFalse;
13105
13106             if (next_image->rendering_intent !=
13107                 next_image->next->rendering_intent)
13108               mng_info->equal_srgbs=MagickFalse;
13109
13110             if ((next_image->units != next_image->next->units) ||
13111                 (next_image->resolution.x != next_image->next->resolution.x) ||
13112                 (next_image->resolution.y != next_image->next->resolution.y))
13113               mng_info->equal_physs=MagickFalse;
13114
13115             if (mng_info->equal_chrms)
13116               {
13117                 if (next_image->chromaticity.red_primary.x !=
13118                     next_image->next->chromaticity.red_primary.x ||
13119                     next_image->chromaticity.red_primary.y !=
13120                     next_image->next->chromaticity.red_primary.y ||
13121                     next_image->chromaticity.green_primary.x !=
13122                     next_image->next->chromaticity.green_primary.x ||
13123                     next_image->chromaticity.green_primary.y !=
13124                     next_image->next->chromaticity.green_primary.y ||
13125                     next_image->chromaticity.blue_primary.x !=
13126                     next_image->next->chromaticity.blue_primary.x ||
13127                     next_image->chromaticity.blue_primary.y !=
13128                     next_image->next->chromaticity.blue_primary.y ||
13129                     next_image->chromaticity.white_point.x !=
13130                     next_image->next->chromaticity.white_point.x ||
13131                     next_image->chromaticity.white_point.y !=
13132                     next_image->next->chromaticity.white_point.y)
13133                   mng_info->equal_chrms=MagickFalse;
13134               }
13135           }
13136         image_count++;
13137         next_image=GetNextImageInList(next_image);
13138       }
13139       if (image_count < 2)
13140         {
13141           mng_info->equal_backgrounds=MagickFalse;
13142           mng_info->equal_chrms=MagickFalse;
13143           mng_info->equal_gammas=MagickFalse;
13144           mng_info->equal_srgbs=MagickFalse;
13145           mng_info->equal_physs=MagickFalse;
13146           use_global_plte=MagickFalse;
13147 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13148           need_local_plte=MagickTrue;
13149 #endif
13150           need_iterations=MagickFalse;
13151         }
13152
13153      if (mng_info->need_fram == MagickFalse)
13154        {
13155          /*
13156            Only certain framing rates 100/n are exactly representable without
13157            the FRAM chunk but we'll allow some slop in VLC files
13158          */
13159          if (final_delay == 0)
13160            {
13161              if (need_iterations != MagickFalse)
13162                {
13163                  /*
13164                    It's probably a GIF with loop; don't run it *too* fast.
13165                  */
13166                  if (mng_info->adjoin)
13167                    {
13168                      final_delay=10;
13169                      (void) ThrowMagickException(exception,GetMagickModule(),
13170                        CoderWarning,
13171                        "input has zero delay between all frames; assuming",
13172                        " 10 cs `%s'","");
13173                    }
13174                }
13175              else
13176                mng_info->ticks_per_second=0;
13177            }
13178          if (final_delay != 0)
13179            mng_info->ticks_per_second=(png_uint_32)
13180               (image->ticks_per_second/final_delay);
13181          if (final_delay > 50)
13182            mng_info->ticks_per_second=2;
13183
13184          if (final_delay > 75)
13185            mng_info->ticks_per_second=1;
13186
13187          if (final_delay > 125)
13188            mng_info->need_fram=MagickTrue;
13189
13190          if (need_defi && final_delay > 2 && (final_delay != 4) &&
13191             (final_delay != 5) && (final_delay != 10) && (final_delay != 20) &&
13192             (final_delay != 25) && (final_delay != 50) &&
13193             (final_delay != (size_t) image->ticks_per_second))
13194            mng_info->need_fram=MagickTrue;  /* make it exact; cannot be VLC */
13195        }
13196
13197      if (mng_info->need_fram != MagickFalse)
13198         mng_info->ticks_per_second=image->ticks_per_second;
13199      /*
13200         If pseudocolor, we should also check to see if all the
13201         palettes are identical and write a global PLTE if they are.
13202         ../glennrp Feb 99.
13203      */
13204      /*
13205         Write the MNG version 1.0 signature and MHDR chunk.
13206      */
13207      (void) WriteBlob(image,8,(const unsigned char *) "\212MNG\r\n\032\n");
13208      (void) WriteBlobMSBULong(image,28L);  /* chunk data length=28 */
13209      PNGType(chunk,mng_MHDR);
13210      LogPNGChunk(logging,mng_MHDR,28L);
13211      PNGLong(chunk+4,(png_uint_32) mng_info->page.width);
13212      PNGLong(chunk+8,(png_uint_32) mng_info->page.height);
13213      PNGLong(chunk+12,mng_info->ticks_per_second);
13214      PNGLong(chunk+16,0L);  /* layer count=unknown */
13215      PNGLong(chunk+20,0L);  /* frame count=unknown */
13216      PNGLong(chunk+24,0L);  /* play time=unknown   */
13217      if (write_jng)
13218        {
13219          if (need_matte)
13220            {
13221              if (need_defi || mng_info->need_fram || use_global_plte)
13222                PNGLong(chunk+28,27L);    /* simplicity=LC+JNG */
13223
13224              else
13225                PNGLong(chunk+28,25L);    /* simplicity=VLC+JNG */
13226            }
13227
13228          else
13229            {
13230              if (need_defi || mng_info->need_fram || use_global_plte)
13231                PNGLong(chunk+28,19L);  /* simplicity=LC+JNG, no transparency */
13232
13233              else
13234                PNGLong(chunk+28,17L);  /* simplicity=VLC+JNG, no transparency */
13235            }
13236        }
13237
13238      else
13239        {
13240          if (need_matte)
13241            {
13242              if (need_defi || mng_info->need_fram || use_global_plte)
13243                PNGLong(chunk+28,11L);    /* simplicity=LC */
13244
13245              else
13246                PNGLong(chunk+28,9L);    /* simplicity=VLC */
13247            }
13248
13249          else
13250            {
13251              if (need_defi || mng_info->need_fram || use_global_plte)
13252                PNGLong(chunk+28,3L);    /* simplicity=LC, no transparency */
13253
13254              else
13255                PNGLong(chunk+28,1L);    /* simplicity=VLC, no transparency */
13256            }
13257        }
13258      (void) WriteBlob(image,32,chunk);
13259      (void) WriteBlobMSBULong(image,crc32(0,chunk,32));
13260      option=GetImageOption(image_info,"mng:need-cacheoff");
13261      if (option != (const char *) NULL)
13262        {
13263          size_t
13264            length;
13265
13266          /*
13267            Write "nEED CACHEOFF" to turn playback caching off for streaming MNG.
13268          */
13269          PNGType(chunk,mng_nEED);
13270          length=CopyMagickString((char *) chunk+4,"CACHEOFF",20);
13271          (void) WriteBlobMSBULong(image,(size_t) length);
13272          LogPNGChunk(logging,mng_nEED,(size_t) length);
13273          length+=4;
13274          (void) WriteBlob(image,length,chunk);
13275          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) length));
13276        }
13277      if ((GetPreviousImageInList(image) == (Image *) NULL) &&
13278          (GetNextImageInList(image) != (Image *) NULL) &&
13279          (image->iterations != 1))
13280        {
13281          /*
13282            Write MNG TERM chunk
13283          */
13284          (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13285          PNGType(chunk,mng_TERM);
13286          LogPNGChunk(logging,mng_TERM,10L);
13287          chunk[4]=3;  /* repeat animation */
13288          chunk[5]=0;  /* show last frame when done */
13289          PNGLong(chunk+6,(png_uint_32) (mng_info->ticks_per_second*
13290             final_delay/MagickMax(image->ticks_per_second,1)));
13291
13292          if (image->iterations == 0)
13293            PNGLong(chunk+10,PNG_UINT_31_MAX);
13294
13295          else
13296            PNGLong(chunk+10,(png_uint_32) image->iterations);
13297
13298          if (logging != MagickFalse)
13299            {
13300              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13301                "     TERM delay: %.20g",(double) (mng_info->ticks_per_second*
13302               final_delay/MagickMax(image->ticks_per_second,1)));
13303
13304              if (image->iterations == 0)
13305                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13306                  "     TERM iterations: %.20g",(double) PNG_UINT_31_MAX);
13307
13308              else
13309                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13310                  "     Image iterations: %.20g",(double) image->iterations);
13311            }
13312          (void) WriteBlob(image,14,chunk);
13313          (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13314        }
13315      /*
13316        To do: check for cHRM+gAMA == sRGB, and write sRGB instead.
13317      */
13318      if ((image->colorspace == sRGBColorspace || image->rendering_intent) &&
13319           mng_info->equal_srgbs)
13320        {
13321          /*
13322            Write MNG sRGB chunk
13323          */
13324          (void) WriteBlobMSBULong(image,1L);
13325          PNGType(chunk,mng_sRGB);
13326          LogPNGChunk(logging,mng_sRGB,1L);
13327
13328          if (image->rendering_intent != UndefinedIntent)
13329            chunk[4]=(unsigned char)
13330              Magick_RenderingIntent_to_PNG_RenderingIntent(
13331              (image->rendering_intent));
13332
13333          else
13334            chunk[4]=(unsigned char)
13335              Magick_RenderingIntent_to_PNG_RenderingIntent(
13336                (PerceptualIntent));
13337
13338          (void) WriteBlob(image,5,chunk);
13339          (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13340          mng_info->have_write_global_srgb=MagickTrue;
13341        }
13342
13343      else
13344        {
13345          if (image->gamma && mng_info->equal_gammas)
13346            {
13347              /*
13348                 Write MNG gAMA chunk
13349              */
13350              (void) WriteBlobMSBULong(image,4L);
13351              PNGType(chunk,mng_gAMA);
13352              LogPNGChunk(logging,mng_gAMA,4L);
13353              PNGLong(chunk+4,(png_uint_32) (100000*image->gamma+0.5));
13354              (void) WriteBlob(image,8,chunk);
13355              (void) WriteBlobMSBULong(image,crc32(0,chunk,8));
13356              mng_info->have_write_global_gama=MagickTrue;
13357            }
13358          if (mng_info->equal_chrms)
13359            {
13360              PrimaryInfo
13361                primary;
13362
13363              /*
13364                 Write MNG cHRM chunk
13365              */
13366              (void) WriteBlobMSBULong(image,32L);
13367              PNGType(chunk,mng_cHRM);
13368              LogPNGChunk(logging,mng_cHRM,32L);
13369              primary=image->chromaticity.white_point;
13370              PNGLong(chunk+4,(png_uint_32) (100000*primary.x+0.5));
13371              PNGLong(chunk+8,(png_uint_32) (100000*primary.y+0.5));
13372              primary=image->chromaticity.red_primary;
13373              PNGLong(chunk+12,(png_uint_32) (100000*primary.x+0.5));
13374              PNGLong(chunk+16,(png_uint_32) (100000*primary.y+0.5));
13375              primary=image->chromaticity.green_primary;
13376              PNGLong(chunk+20,(png_uint_32) (100000*primary.x+0.5));
13377              PNGLong(chunk+24,(png_uint_32) (100000*primary.y+0.5));
13378              primary=image->chromaticity.blue_primary;
13379              PNGLong(chunk+28,(png_uint_32) (100000*primary.x+0.5));
13380              PNGLong(chunk+32,(png_uint_32) (100000*primary.y+0.5));
13381              (void) WriteBlob(image,36,chunk);
13382              (void) WriteBlobMSBULong(image,crc32(0,chunk,36));
13383              mng_info->have_write_global_chrm=MagickTrue;
13384            }
13385        }
13386      if (image->resolution.x && image->resolution.y && mng_info->equal_physs)
13387        {
13388          /*
13389             Write MNG pHYs chunk
13390          */
13391          (void) WriteBlobMSBULong(image,9L);
13392          PNGType(chunk,mng_pHYs);
13393          LogPNGChunk(logging,mng_pHYs,9L);
13394
13395          if (image->units == PixelsPerInchResolution)
13396            {
13397              PNGLong(chunk+4,(png_uint_32)
13398                (image->resolution.x*100.0/2.54+0.5));
13399
13400              PNGLong(chunk+8,(png_uint_32)
13401                (image->resolution.y*100.0/2.54+0.5));
13402
13403              chunk[12]=1;
13404            }
13405
13406          else
13407            {
13408              if (image->units == PixelsPerCentimeterResolution)
13409                {
13410                  PNGLong(chunk+4,(png_uint_32)
13411                    (image->resolution.x*100.0+0.5));
13412
13413                  PNGLong(chunk+8,(png_uint_32)
13414                    (image->resolution.y*100.0+0.5));
13415
13416                  chunk[12]=1;
13417                }
13418
13419              else
13420                {
13421                  PNGLong(chunk+4,(png_uint_32) (image->resolution.x+0.5));
13422                  PNGLong(chunk+8,(png_uint_32) (image->resolution.y+0.5));
13423                  chunk[12]=0;
13424                }
13425            }
13426          (void) WriteBlob(image,13,chunk);
13427          (void) WriteBlobMSBULong(image,crc32(0,chunk,13));
13428        }
13429      /*
13430        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
13431        or does not cover the entire frame.
13432      */
13433      if (write_mng && ((image->alpha_trait != UndefinedPixelTrait) ||
13434          image->page.x > 0 || image->page.y > 0 || (image->page.width &&
13435          (image->page.width+image->page.x < mng_info->page.width))
13436          || (image->page.height && (image->page.height+image->page.y
13437          < mng_info->page.height))))
13438        {
13439          (void) WriteBlobMSBULong(image,6L);
13440          PNGType(chunk,mng_BACK);
13441          LogPNGChunk(logging,mng_BACK,6L);
13442          red=ScaleQuantumToShort(image->background_color.red);
13443          green=ScaleQuantumToShort(image->background_color.green);
13444          blue=ScaleQuantumToShort(image->background_color.blue);
13445          PNGShort(chunk+4,red);
13446          PNGShort(chunk+6,green);
13447          PNGShort(chunk+8,blue);
13448          (void) WriteBlob(image,10,chunk);
13449          (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13450          if (mng_info->equal_backgrounds)
13451            {
13452              (void) WriteBlobMSBULong(image,6L);
13453              PNGType(chunk,mng_bKGD);
13454              LogPNGChunk(logging,mng_bKGD,6L);
13455              (void) WriteBlob(image,10,chunk);
13456              (void) WriteBlobMSBULong(image,crc32(0,chunk,10));
13457            }
13458        }
13459
13460 #ifdef PNG_WRITE_EMPTY_PLTE_SUPPORTED
13461      if ((need_local_plte == MagickFalse) &&
13462          (image->storage_class == PseudoClass) &&
13463          (all_images_are_gray == MagickFalse))
13464        {
13465          size_t
13466            data_length;
13467
13468          /*
13469            Write MNG PLTE chunk
13470          */
13471          data_length=3*image->colors;
13472          (void) WriteBlobMSBULong(image,data_length);
13473          PNGType(chunk,mng_PLTE);
13474          LogPNGChunk(logging,mng_PLTE,data_length);
13475
13476          for (i=0; i < (ssize_t) image->colors; i++)
13477          {
13478            chunk[4+i*3]=(unsigned char) (ScaleQuantumToChar(
13479              image->colormap[i].red) & 0xff);
13480            chunk[5+i*3]=(unsigned char) (ScaleQuantumToChar(
13481              image->colormap[i].green) & 0xff);
13482            chunk[6+i*3]=(unsigned char) (ScaleQuantumToChar(
13483              image->colormap[i].blue) & 0xff);
13484          }
13485
13486          (void) WriteBlob(image,data_length+4,chunk);
13487          (void) WriteBlobMSBULong(image,crc32(0,chunk,(uInt) (data_length+4)));
13488          mng_info->have_write_global_plte=MagickTrue;
13489        }
13490 #endif
13491     }
13492   scene=0;
13493   mng_info->delay=0;
13494 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13495     defined(PNG_MNG_FEATURES_SUPPORTED)
13496   mng_info->equal_palettes=MagickFalse;
13497 #endif
13498   do
13499   {
13500     if (mng_info->adjoin)
13501     {
13502 #if defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) || \
13503     defined(PNG_MNG_FEATURES_SUPPORTED)
13504     /*
13505       If we aren't using a global palette for the entire MNG, check to
13506       see if we can use one for two or more consecutive images.
13507     */
13508     if (need_local_plte && use_global_plte && !all_images_are_gray)
13509       {
13510         if (mng_info->IsPalette)
13511           {
13512             /*
13513               When equal_palettes is true, this image has the same palette
13514               as the previous PseudoClass image
13515             */
13516             mng_info->have_write_global_plte=mng_info->equal_palettes;
13517             mng_info->equal_palettes=PalettesAreEqual(image,image->next);
13518             if (mng_info->equal_palettes && !mng_info->have_write_global_plte)
13519               {
13520                 /*
13521                   Write MNG PLTE chunk
13522                 */
13523                 size_t
13524                   data_length;
13525
13526                 data_length=3*image->colors;
13527                 (void) WriteBlobMSBULong(image,data_length);
13528                 PNGType(chunk,mng_PLTE);
13529                 LogPNGChunk(logging,mng_PLTE,data_length);
13530
13531                 for (i=0; i < (ssize_t) image->colors; i++)
13532                 {
13533                   chunk[4+i*3]=ScaleQuantumToChar(image->colormap[i].red);
13534                   chunk[5+i*3]=ScaleQuantumToChar(image->colormap[i].green);
13535                   chunk[6+i*3]=ScaleQuantumToChar(image->colormap[i].blue);
13536                 }
13537
13538                 (void) WriteBlob(image,data_length+4,chunk);
13539                 (void) WriteBlobMSBULong(image,crc32(0,chunk,
13540                    (uInt) (data_length+4)));
13541                 mng_info->have_write_global_plte=MagickTrue;
13542               }
13543           }
13544         else
13545           mng_info->have_write_global_plte=MagickFalse;
13546       }
13547 #endif
13548     if (need_defi)
13549       {
13550         ssize_t
13551           previous_x,
13552           previous_y;
13553
13554         if (scene)
13555           {
13556             previous_x=mng_info->page.x;
13557             previous_y=mng_info->page.y;
13558           }
13559         else
13560           {
13561             previous_x=0;
13562             previous_y=0;
13563           }
13564         mng_info->page=image->page;
13565         if ((mng_info->page.x !=  previous_x) ||
13566             (mng_info->page.y != previous_y))
13567           {
13568              (void) WriteBlobMSBULong(image,12L);  /* data length=12 */
13569              PNGType(chunk,mng_DEFI);
13570              LogPNGChunk(logging,mng_DEFI,12L);
13571              chunk[4]=0; /* object 0 MSB */
13572              chunk[5]=0; /* object 0 LSB */
13573              chunk[6]=0; /* visible  */
13574              chunk[7]=0; /* abstract */
13575              PNGLong(chunk+8,(png_uint_32) mng_info->page.x);
13576              PNGLong(chunk+12,(png_uint_32) mng_info->page.y);
13577              (void) WriteBlob(image,16,chunk);
13578              (void) WriteBlobMSBULong(image,crc32(0,chunk,16));
13579           }
13580       }
13581     }
13582
13583    mng_info->write_mng=write_mng;
13584
13585    if ((int) image->dispose >= 3)
13586      mng_info->framing_mode=3;
13587
13588    if (mng_info->need_fram && mng_info->adjoin &&
13589        ((image->delay != mng_info->delay) ||
13590         (mng_info->framing_mode != mng_info->old_framing_mode)))
13591      {
13592        if (image->delay == mng_info->delay)
13593          {
13594            /*
13595              Write a MNG FRAM chunk with the new framing mode.
13596            */
13597            (void) WriteBlobMSBULong(image,1L);  /* data length=1 */
13598            PNGType(chunk,mng_FRAM);
13599            LogPNGChunk(logging,mng_FRAM,1L);
13600            chunk[4]=(unsigned char) mng_info->framing_mode;
13601            (void) WriteBlob(image,5,chunk);
13602            (void) WriteBlobMSBULong(image,crc32(0,chunk,5));
13603          }
13604        else
13605          {
13606            /*
13607              Write a MNG FRAM chunk with the delay.
13608            */
13609            (void) WriteBlobMSBULong(image,10L);  /* data length=10 */
13610            PNGType(chunk,mng_FRAM);
13611            LogPNGChunk(logging,mng_FRAM,10L);
13612            chunk[4]=(unsigned char) mng_info->framing_mode;
13613            chunk[5]=0;  /* frame name separator (no name) */
13614            chunk[6]=2;  /* flag for changing default delay */
13615            chunk[7]=0;  /* flag for changing frame timeout */
13616            chunk[8]=0;  /* flag for changing frame clipping */
13617            chunk[9]=0;  /* flag for changing frame sync_id */
13618            PNGLong(chunk+10,(png_uint_32)
13619              ((mng_info->ticks_per_second*
13620              image->delay)/MagickMax(image->ticks_per_second,1)));
13621            (void) WriteBlob(image,14,chunk);
13622            (void) WriteBlobMSBULong(image,crc32(0,chunk,14));
13623            mng_info->delay=(png_uint_32) image->delay;
13624          }
13625        mng_info->old_framing_mode=mng_info->framing_mode;
13626      }
13627
13628 #if defined(JNG_SUPPORTED)
13629    if (image_info->compression == JPEGCompression)
13630      {
13631        ImageInfo
13632          *write_info;
13633
13634        if (logging != MagickFalse)
13635          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13636            "  Writing JNG object.");
13637        /* To do: specify the desired alpha compression method. */
13638        write_info=CloneImageInfo(image_info);
13639        write_info->compression=UndefinedCompression;
13640        status=WriteOneJNGImage(mng_info,write_info,image,exception);
13641        write_info=DestroyImageInfo(write_info);
13642      }
13643    else
13644 #endif
13645      {
13646        if (logging != MagickFalse)
13647          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
13648            "  Writing PNG object.");
13649
13650        mng_info->need_blob = MagickFalse;
13651        mng_info->ping_preserve_colormap = MagickFalse;
13652
13653        /* We don't want any ancillary chunks written */
13654        mng_info->ping_exclude_bKGD=MagickTrue;
13655        mng_info->ping_exclude_cHRM=MagickTrue;
13656        mng_info->ping_exclude_date=MagickTrue;
13657        mng_info->ping_exclude_EXIF=MagickTrue;
13658        mng_info->ping_exclude_gAMA=MagickTrue;
13659        mng_info->ping_exclude_iCCP=MagickTrue;
13660        /* mng_info->ping_exclude_iTXt=MagickTrue; */
13661        mng_info->ping_exclude_oFFs=MagickTrue;
13662        mng_info->ping_exclude_pHYs=MagickTrue;
13663        mng_info->ping_exclude_sRGB=MagickTrue;
13664        mng_info->ping_exclude_tEXt=MagickTrue;
13665        mng_info->ping_exclude_tRNS=MagickTrue;
13666        mng_info->ping_exclude_vpAg=MagickTrue;
13667        mng_info->ping_exclude_zCCP=MagickTrue;
13668        mng_info->ping_exclude_zTXt=MagickTrue;
13669
13670        status=WriteOnePNGImage(mng_info,image_info,image,exception);
13671      }
13672
13673     if (status == MagickFalse)
13674       {
13675         MngInfoFreeStruct(mng_info,&have_mng_structure);
13676         (void) CloseBlob(image);
13677         return(MagickFalse);
13678       }
13679     (void) CatchImageException(image);
13680     if (GetNextImageInList(image) == (Image *) NULL)
13681       break;
13682     image=SyncNextImageInList(image);
13683     status=SetImageProgress(image,SaveImagesTag,scene++,
13684       GetImageListLength(image));
13685
13686     if (status == MagickFalse)
13687       break;
13688
13689   } while (mng_info->adjoin);
13690
13691   if (write_mng)
13692     {
13693       while (GetPreviousImageInList(image) != (Image *) NULL)
13694         image=GetPreviousImageInList(image);
13695       /*
13696         Write the MEND chunk.
13697       */
13698       (void) WriteBlobMSBULong(image,0x00000000L);
13699       PNGType(chunk,mng_MEND);
13700       LogPNGChunk(logging,mng_MEND,0L);
13701       (void) WriteBlob(image,4,chunk);
13702       (void) WriteBlobMSBULong(image,crc32(0,chunk,4));
13703     }
13704   /*
13705     Relinquish resources.
13706   */
13707   (void) CloseBlob(image);
13708   MngInfoFreeStruct(mng_info,&have_mng_structure);
13709
13710   if (logging != MagickFalse)
13711     (void) LogMagickEvent(CoderEvent,GetMagickModule(),"exit WriteMNGImage()");
13712
13713   return(MagickTrue);
13714 }
13715 #else /* PNG_LIBPNG_VER > 10011 */
13716
13717 static MagickBooleanType WritePNGImage(const ImageInfo *image_info,Image *image)
13718 {
13719   (void) image;
13720   printf("Your PNG library is too old: You have libpng-%s\n",
13721      PNG_LIBPNG_VER_STRING);
13722
13723   ThrowBinaryException(CoderError,"PNG library is too old",
13724      image_info->filename);
13725 }
13726
13727 static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image)
13728 {
13729   return(WritePNGImage(image_info,image));
13730 }
13731 #endif /* PNG_LIBPNG_VER > 10011 */
13732 #endif