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