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